/*
 * funciones.c
 *
 *  Created on: 15-jul-2009
 *      Author: Pablo Izquierdo & Adrián Romero
 *
 *      Implementacion de las funciones definidas en funciones.h
 */

#include "funciones.h"
#include "constantes.h"
#include "estructuras.h"

int descriptorSocketNivelEnlace, descriptorSocketArp;
struct sockaddr_ll socketNivelEnlace, socketArp;
int descriptorSocketDatagramasUDP;

void bajarInterfaz(){
	int descriptorSocket;
	struct ifreq ifr;
	int estaLevantada;
	descriptorSocket = socket(PF_INET, SOCK_DGRAM, 0);
	strcpy(ifr.ifr_name, PARAMETRO_IFACE);
	ioctl(descriptorSocket, SIOCGIFFLAGS, &ifr);//obtiene flags
	estaLevantada = ifr.ifr_ifru.ifru_flags & IFF_UP;//mira si esta levantada
	if(estaLevantada == 1){
		ifr.ifr_ifru.ifru_flags = ifr.ifr_ifru.ifru_flags & !IFF_UP;
		ioctl(descriptorSocket, SIOCSIFFLAGS, &ifr);//establece la interfaz como bajada
	}
	close(descriptorSocket);
}

unsigned short calcularChecksum(unsigned short *addr, int len) {
	int nleft = len;
	int sum = 0;
	unsigned short *w = addr;
	unsigned short answer = 0;

	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(unsigned char *) (&answer) = *(unsigned char *) w;
		sum += answer;
	}

	sum = (sum >> 16) + (sum & 0xFFFF);
	sum += (sum >> 16);
	answer = ~sum;
	return (answer);
}

int calcularTamMensajeDhcp(struct DatosDhcp *datos){
	int resultado;
	resultado = datos->longitudCampoOpciones + DHCP_TAM_BASE; //En octetos
	return resultado;
}

int compararDirHw(char * had){
	int i, resultado;
	resultado = TRUE;
	for(i = 0; resultado == TRUE && i < TAM_DIRECCION_HW; i++){
		if(had[i]!= DIRECCION_HW[i])
			resultado = FALSE;
	}
	imprimirDebug("compararDirHw","resultado %d",resultado);
	return resultado;
}

void cerrarSocketNivelEnlace(){
	if (close(descriptorSocketNivelEnlace) < 0) {
		perror("FALLO AL CERRAR EL SOCKET DE BAJO NIVEL => funciones.c");
	}
	if (close(descriptorSocketArp) < 0) {
		perror("FALLO AL CERRAR EL SOCKET DE arp => funciones.c");
	}
}

struct DatosArp* crearNuevoArp() {
	struct DatosArp *nuevoArp;
	nuevoArp=malloc(sizeof(struct DatosArp));
	nuevoArp->htype=0;
	nuevoArp->ptype=0;
	nuevoArp->hlen=0;
	nuevoArp->plen=0;
	nuevoArp->oper=0;
	bzero(nuevoArp->sha, 6);
	nuevoArp->spa=0;
	bzero(nuevoArp->tha, 6);
	nuevoArp->tpa=0;
	return nuevoArp;
}

struct DatosDhcp* crearNuevoDhcp() {
	struct DatosDhcp *nuevoMensaje;
	nuevoMensaje = malloc(sizeof(struct DatosDhcp));
	nuevoMensaje->op = 0;
	nuevoMensaje->htype = 0;
	nuevoMensaje->hlen = 0;
	nuevoMensaje->hops = 0;
	nuevoMensaje->xid = 0;
	nuevoMensaje->secs = 0;
	nuevoMensaje->flags = 0;
	nuevoMensaje->ciaddr = 0;
	nuevoMensaje->yiaddr = 0;
	nuevoMensaje->siaddr = 0;
	nuevoMensaje->giaddr = 0;
	bzero(nuevoMensaje->chaddr, 16);
	bzero(nuevoMensaje->sname, 64);
	bzero(nuevoMensaje->file, 128);
	nuevoMensaje->longitudCampoOpciones = 0;
	nuevoMensaje->options = NULL;
	return nuevoMensaje;
}

int enviarArp(){
	struct DatosArp *arpRequest;
	unsigned char** mensaje;
	size_t tamMensaje;
	int resultado, enviado, aleatorio, i;
	int probeWaitMilisegundos, announceWaitMilisegundos, announceIntervalMilisegundos;
	fd_set conjuntoRecibidos; //conjunto de descriptores de entrada
	struct timeval umbral; //intervalo maximo de espera para completar la seleccion
	char * mensajeReply;
	char * buf;
	int arpReply = -1;

	// Se establecen los sets de descriptores
	FD_ZERO(&conjuntoRecibidos); //inicializa el conjunto de descriptores a un conjunto null
	FD_SET(descriptorSocketArp, &conjuntoRecibidos); //incluye el descriptorSocketNivelEnlace en el conjunto conjuntoRecibidos de descriptores a ser leidos

	//esperar un tiempo aleatorio entre 0 y PROBE_WAIT
	probeWaitMilisegundos = 1000 * PROBE_WAIT;
	srandom(time(NULL));
	aleatorio = random() % probeWaitMilisegundos;
	esperar(aleatorio);

	arpRequest = crearNuevoArp();
	arpRequest->htype = 1;
	arpRequest->ptype = 2048;//0x0800
	arpRequest->hlen = 6;
	arpRequest->plen = 4;
	arpRequest->oper = 1;
	memcpy(arpRequest->sha, DIRECCION_HW, arpRequest->hlen);
	arpRequest->tpa = DIRECCION_SELECCIONADA.s_addr;

	resultado = 0;
	//Define mensaje e inclusion de cabeceras
	mensaje = malloc(4);
	tamMensaje = obtenerMensajeArp(mensaje, arpRequest);//guarda el contenido de datos en mensaje con todas las cabeceras
	imprimirDebug("enviarArp", "El tamaño del paquete es %d", tamMensaje);
	mensajeReply = malloc(tamMensaje);
	bzero(mensajeReply, tamMensaje);
	buf = malloc(1000);

	// Se realiza el envio
	i = 0;
	//se envian PROBE_NUM arp probes (3)
	while (i < 3) {
		i++;
//		imprimirTraza(XID, ARP, NULL);
	//	enviado = sendto(descriptorSocketArp, *mensaje, tamMensaje, 0, (struct sockaddr *) &socketArp, sizeof(struct sockaddr_ll));
		enviado = write(descriptorSocketArp, *mensaje, tamMensaje);
		if (enviado == -1) {//ERROR EN EL SENDTO
			perror("FALLO sendto DATOS DEL MENSAJE ETHERNET => funciones.c");
			resultado = -1;
		}
		else {
			imprimirDebug("enviarArp", "Enviado ARP probe %d", enviado);
//			umbral.tv_sec = PROBE_MAX;
			umbral.tv_sec = PROBE_MIN;
			umbral.tv_usec = 0;

			resultado = 1;

			if (i == 3) {
				umbral.tv_sec = ANNOUNCE_WAIT;
			}

			while (resultado > 0 && arpReply != 1) {
				imprimirDebug("enviarArp", "umbral antes de escuchar %d", umbral.tv_sec);
				resultado = select(descriptorSocketArp + 1, &conjuntoRecibidos, NULL, NULL, &umbral);//resultado=0 si el limite de tiempo expira

//				umbral.tv_sec -= 1; //sobra porque lo está decrementando el propio select de 1 en 1

				imprimirDebug("enviarArp", "umbral despues de escuchar %d", umbral.tv_sec);
				imprimirDebug("enviarArp", "resultado %d", resultado);
				if(resultado < 0) {
					perror("select");
				}
				else if(resultado > 0) {
					// Lee un buffer de 1000 bytes
	//				int bytesRecibidos = recvfrom(descriptorSocketArp, buf, 1000, 0, NULL, NULL);
					int bytesRecibidos = read(descriptorSocketArp, mensajeReply, tamMensaje);
					imprimirDebug("enviarArp", "recibido %d",bytesRecibidos);
//					arpReply = esArpReply(buf, bytesRecibidos);//TENGO QUE COMPROBAR REPLY
					arpReply = esArpReply(mensajeReply, bytesRecibidos);//TENGO QUE COMPROBAR REPLY
	//				arpReply = 1;
					if(arpReply == TRUE){//
						//HAY QUE BUSCARSE OTRA DIRECCION IP
						imprimirDebug("enviarArp", "Se ha recibido ARP\n");
					}
					else {
						imprimirDebug("enviarArp", "No se ha recibido ARP\n");
					}
				}
			}
		}
//		imprimirDebug("enviarArp", "Enviado ARP probe %d", enviado);
	}

//	free(*mensaje);

	//se envian announcements
	//espera un tiempo announce wait
	announceWaitMilisegundos = 1000 * ANNOUNCE_WAIT;
//	esperar(announceWaitMilisegundos);

	//manda los announcements sólo si no ha llegado un ARP
	if (arpReply != 1) {
		free(*mensaje);
		arpRequest->spa = DIRECCION_SELECCIONADA.s_addr;
		tamMensaje = obtenerMensajeArp(mensaje, arpRequest);//guarda el contenido de datos en mensaje con todas las cabeceras
		imprimirDebug("enviarArp", "El tamaño del paquete es %d", tamMensaje);
		//esperar announceIntervalMilisegundos
		announceIntervalMilisegundos = 1000 * ANNOUNCE_INTERVAL;

		i = 0;
		while (i < 2) {
			i++;
	//		imprimirTraza(XID, ARP, NULL);
			enviado = sendto(descriptorSocketArp, *mensaje, tamMensaje, 0, (struct sockaddr *) &socketArp, sizeof(struct sockaddr_ll));
			if (enviado == -1) {//ERROR EN EL SENDTO
				perror("FALLO sendto DATOS DEL MENSAJE ETHERNET => funciones.c");
				resultado = -1;
			}
			imprimirDebug("enviarArp", "Enviado ARP announcement %d", enviado);
			esperar(announceIntervalMilisegundos);
		}
	}
//	arpRequest->spa = DIRECCION_SELECCIONADA.s_addr;
//	tamMensaje = obtenerMensajeArp(mensaje, arpRequest);//guarda el contenido de datos en mensaje con todas las cabeceras
//	imprimirDebug("enviarArp", "El tamaño del paquete es %d", tamMensaje);
//	//esperar announceIntervalMilisegundos
//	announceIntervalMilisegundos = 1000 * ANNOUNCE_INTERVAL;
//
//	i = 0;
//	while (i < 2) {
//		i++;
////		imprimirTraza(XID, ARP, NULL);
//		enviado = sendto(descriptorSocketArp, *mensaje, tamMensaje, 0, (struct sockaddr *) &socketArp, sizeof(struct sockaddr_ll));
//		if (enviado == -1) {//ERROR EN EL SENDTO
//			perror("FALLO sendto DATOS DEL MENSAJE ETHERNET => funciones.c");
//			resultado = -1;
//		}
//		imprimirDebug("enviarArp", "Enviado ARP announcement %d", enviado);
//		esperar(announceIntervalMilisegundos);
//	}

	if (arpReply == 1) {
		resultado = 1;
	}
	else {
		resultado = 0;
	}
	free(buf);
	free(*mensaje);
	free(mensajeReply);
	free(mensaje);
	free(arpRequest);
	return resultado;
}

//Envia un mensaje DHCPDECLINE
int enviarDecline(){
	int resultado = EXIT_ERROR;
	struct DatosDhcp * dhcpDecline;
	char ** opciones;
	int tamOpciones;
	char * msgString;

	msgString = malloc(60);
	bzero(msgString, 60);
	// Se crea la estructura del mensaje con los datos adecuados
	dhcpDecline = crearNuevoDhcp();
	dhcpDecline->op = OP_BOOTREQUEST;
	dhcpDecline->htype = HTYPE_ETHERNET;
	dhcpDecline->hlen = HTYPE_ETHERNET_LENGTH;
	dhcpDecline->xid = XID;
	dhcpDecline->secs = 0;
	memcpy(dhcpDecline->chaddr, DIRECCION_HW, dhcpDecline->hlen);
	//magic cookie
	opciones = malloc(4);
	tamOpciones = obtenerTamOpcionesDecline(opciones);//POR IMPLEMENTAR

	dhcpDecline->options = *opciones;
	dhcpDecline->longitudCampoOpciones = tamOpciones;
	// Se envia el mensaje dhcp discover a broadcast
	sprintf(msgString, "%s rejected", inet_ntoa(DIRECCION_SELECCIONADA));
	imprimirTraza(XID, DHCPDECLINE, msgString);
//	imprimirTraza(XID, DHCPDECLINE, inet_ntoa(DIRECCION_SERVIDOR));
//	in_addr_t dirAux = DIRECCION_SERVIDOR.s_addr;
	if (enviarMensajeEthernet(dhcpDecline, INADDR_BROADCAST) >= 0) {
//	if (enviarMensajeEthernet(dhcpDecline, dirAux) >= 0) {
		resultado = TRUE;
	}
	else {
		fprintf(stderr,
		"ERROR: No se ha podido mandar el mensaje dhcpDecline.\n");
	}
	free(msgString);
	free(opciones);
	liberarDatosDhcp(dhcpDecline);
	return resultado;
}

//Envia un mensaje DHCPDISCOVER
int enviarDiscover(){
	int resultado = EXIT_ERROR;
	double r;
	struct DatosDhcp * dhcpDiscover;
	char ** opciones;
	int tamOpciones;

	// Se genera un xid aleatorio
	r = (double) random() / (double) RAND_MAX;
	XID = UINT_MAX * r;

	// Se crea la estructura del mensaje con los datos adecuados
	dhcpDiscover = crearNuevoDhcp();
	dhcpDiscover->op = OP_BOOTREQUEST;
	dhcpDiscover->htype = HTYPE_ETHERNET;
	dhcpDiscover->hlen = HTYPE_ETHERNET_LENGTH;
	dhcpDiscover->xid = XID;
	dhcpDiscover->secs = 0;
	dhcpDiscover->flags = 32768;//bcast
	memcpy(dhcpDiscover->chaddr, DIRECCION_HW, dhcpDiscover->hlen);
	//magic cookie
	opciones = malloc(4);
	tamOpciones = obtenerTamOpcionesDiscover(opciones);

	dhcpDiscover->options = *opciones;
	dhcpDiscover->longitudCampoOpciones = tamOpciones;
	// Se envia el mensaje dhcp discover a broadcast
	imprimirTraza(XID, DHCPDISCOVER, NULL);
	if (enviarMensajeEthernet(dhcpDiscover, INADDR_BROADCAST) >= 0) {
		resultado = TRUE;
	}
	else {
		fprintf(stderr,
		"ERROR: No se ha podido mandar el mensaje dhcpDiscover.\n");
	}
	free(opciones);
	liberarDatosDhcp(dhcpDiscover);
	return resultado;
}

int enviarMensajeEthernet(struct DatosDhcp *datos, in_addr_t direccionInternet){
	unsigned char** mensaje;
	size_t tamMensaje;
	int resultado, enviado;
	resultado = 0;
	/// Definimos el mensaje, inclusion de cabeceras...
	mensaje = malloc(4);
	tamMensaje = obtenerMensajeEthernet(mensaje, direccionInternet, datos);//guarda el contenido de datos en mensaje con todas las cabeceras
	imprimirDebug("enviarMensajeEthernet", "El tamaño del paquete es %d", tamMensaje);

	// Se realiza el envio
	enviado = sendto(descriptorSocketNivelEnlace, *mensaje, tamMensaje, 0,
			(struct sockaddr *) &socketNivelEnlace, sizeof(struct sockaddr_ll));//manda el mensaje IP con el socket de nivel de enlace
	if (enviado == -1) {//ERROR EN EL SENDTO
		perror("FALLO sendto DATOS DEL MENSAJE ETHERNET => funciones.c");
		resultado = -1;
	}
	imprimirDebug("enviarMensajeEthernet", "Enviado %d", enviado);

	free(*mensaje);
	free(mensaje);
	return resultado;
}

int enviarMensajeUdp(unsigned char* mensaje, uint longitud, struct in_addr * dirIpDestino) {

	struct sockaddr_in socketDestino;
	struct sockaddr_in socketOrigen;
	int resultado = 0;
	int activado = 1;

	descriptorSocketDatagramasUDP = socket(AF_INET,SOCK_DGRAM, 0);

	setsockopt(descriptorSocketDatagramasUDP, SOL_SOCKET, SO_REUSEADDR, &activado, sizeof(int));
	if (descriptorSocketDatagramasUDP < 0)
		perror("socket, enviarMensajeUdp");
	else {
		socketDestino.sin_addr = *dirIpDestino;
		socketDestino.sin_family = AF_INET;
		socketDestino.sin_port = htons(PUERTO_DHCP_SERVIDOR);

		socketOrigen.sin_addr = DIRECCION_SELECCIONADA;
		socketOrigen.sin_family = AF_INET;
		socketOrigen.sin_port = htons(PUERTO_DHCP_CLIENTE);

		resultado = bind(descriptorSocketDatagramasUDP, (struct sockaddr*) &socketOrigen,
				sizeof(struct sockaddr_in));
		if (resultado < 0)
			perror("bind, enviarMensajeUdp");
		else {
			resultado = sendto(descriptorSocketDatagramasUDP, mensaje, longitud, 0, (struct sockaddr*) &socketDestino,
					sizeof(struct sockaddr_in));
			if (resultado < 0) {
				perror("sendto, enviarMensajeUdp");
			}
		}
	}

	return resultado;
}
//Envia un mensaje DHCPRELEASE
int enviarRelease(){
	struct DatosDhcp * datos;
	struct CadenaDhcp * cadena;
	char ** opciones;
	int tamOpciones;
	int resultado;
	int aux;
	char * msgString;

	imprimirDebug("enviarRelease", "");
	datos = crearNuevoDhcp();

	// Se crea la estructura del mensaje con los datos adecuados
	datos->op = OP_BOOTREQUEST;
	datos->hlen = 6;
	datos->xid = XID;
	datos->secs = 0;
	aux = htonl(DIRECCION_SELECCIONADA.s_addr);//elige la direccion IP que posee para liberarla
	memcpy(&datos->ciaddr, &aux, sizeof(in_addr_t));//copia la direccion en el nuevo DatosDhcp que se va a enviar como DHCPRELEASE
	memcpy(datos->chaddr, DIRECCION_HW, datos->hlen);

	opciones = malloc(4);
	tamOpciones = obtenerTamOpcionesRelease(opciones);//rellena en opciones las opciones que se van a mandar en el DHCPRELEASE
	datos->options = (*opciones);
	datos->longitudCampoOpciones = tamOpciones;

	cadena = pasarDatosDhcpACadena(datos);//

	resultado = enviarMensajeUdp(cadena->contenido, cadena->longitud, &DIRECCION_SERVIDOR);//se envia con socket de datagramas, no de nivel de enlace

	if (resultado >= 0) {
		msgString = malloc(60);
		bzero(msgString, 60);
		sprintf(msgString, "%s", inet_ntoa(DIRECCION_SELECCIONADA));
		imprimirTraza(XID, DHCPRELEASE, msgString);
		free(msgString);
	}

	liberarDatosDhcp(datos);
	liberarCadenaDhcp(cadena);
	free(opciones);
	return resultado;
}

//Envia un mensaje DHCPREQUEST
int enviarRequest(){
	int resultado = EXIT_ERROR;
	struct DatosDhcp* dhcpRequest;
	char ** opciones;
	int tamOpciones;
	char * msgString;

	msgString = malloc(60);
	bzero(msgString, 60);

	imprimirDebug("enviarRequest", "enviando dhcpRequest");

	// Se crea la estructura del mensaje con los datos adecuados
	dhcpRequest = crearNuevoDhcp();

	dhcpRequest->op = OP_BOOTREQUEST;
	dhcpRequest->htype = HTYPE_ETHERNET;
	dhcpRequest->hlen = 6;
	dhcpRequest->xid = XID;
	dhcpRequest->secs = 0;
	if (ESTADO_ACTUAL == SELECTING) {
		dhcpRequest->flags = 0x8000;
	}
	memcpy(dhcpRequest->chaddr, DIRECCION_HW, dhcpRequest->hlen);

	opciones = malloc(4);
	tamOpciones = obtenerTamOpcionesRequest(opciones);
	dhcpRequest->options = (*opciones);
	dhcpRequest->longitudCampoOpciones = tamOpciones;

	// Se envia el mensaje dhcp request a broadcast
	if (enviarMensajeEthernet(dhcpRequest, INADDR_BROADCAST) >= 0) {
		//OJO IMPORTANTE: si hay un fallo de memoria puede ser aqui, no deberia porq in_addr no usa punteros pero
		// hay q estar pendiente
		struct in_addr a;
		a.s_addr=INADDR_BROADCAST;
//		if (ESTADO_ACTUAL == SELECTING) {
			sprintf(msgString, "all (binding %s)", inet_ntoa(DIRECCION_SELECCIONADA));
			imprimirTraza(XID, DHCPREQUEST, msgString);
//			imprimirTraza(XID, DHCPREQUEST, inet_ntoa(a));
//		}
/*		else if (ESTADO_ACTUAL == REBINDING) {
			sprintf(msgString, "all (rebinding %s)", inet_ntoa(DIRECCION_SELECCIONADA));
			imprimirTraza(XID, DHCPREQUEST, msgString);
//			imprimirTraza(XID, DHCPREQUEST, inet_ntoa(a));
		}*/
		resultado = TRUE;
	} else {
		fprintf(stderr,
				"ERROR: No se ha podido mandar el mensaje dhcpRequest.\n");
	}

	free(msgString);
	free(opciones);
	liberarDatosDhcp(dhcpRequest);
	return resultado;
}
int esAck(struct DatosDhcp* dhcp){
	int resultado = -1;
	int pos = 0;
	char actual;

	if(((u_int8_t)dhcp->options[0]) == 0x63 && ((u_int8_t)dhcp->options[1]) == 0x82 && ((u_int8_t)dhcp->options[2]) == 0x53 && ((u_int8_t)dhcp->options[3]) == 0x63){
		pos += 4;
		while(pos < dhcp->longitudCampoOpciones && resultado == -1){
			actual = dhcp->options[pos];
			if(actual == 53){ // Tipo de mensaje (DHCP message type)
				// es ACK
				if(dhcp->options[pos+1] == 1 && dhcp->options[pos+2] == 05) {//5 es DHCPACK
					resultado = 1;
				}
				// es NACK
				else if(dhcp->options[pos+1] == 1 && dhcp->options[pos+2] == 06) {//6 es DHCPNAK
					resultado = 0;
				}
			}
			// Se salta el resto de la opción
			else {
				pos += dhcp->options[pos+1]+2;
			}
		}
	}
	else {
		imprimirDebug("esAck", "Magic cookie incorrecta");
	}
	return resultado;
}

int esArpReply(char* datagrama, int longitud){
	int resultado = FALSE;
//	struct DatosArp da;
//	unsigned long dirIpObjetivo;
	unsigned long dirIpOrigen;
	int indice;

//	if(longitud == TAM_PAQ_ARP && datagrama[4] == 0x06 && datagrama[5] == 0x04 && datagrama[7] == 0x02){ // ARP reply
//	if(longitud == (TAM_CAB_ETH + TAM_PAQ_ARP) && datagrama[12] == 0x08 && datagrama[13] == 0x06 && datagrama[21] == 0x02){ // ARP reply
	if(longitud == (TAM_CAB_ETH + TAM_PAQ_ARP) && datagrama[12] == 0x08 && datagrama[13] == 0x06){ // ARP
		imprimirDebug("esArpReply", "Acaba de llegar un ARP");
		if(datagrama[21] == 0x02){//reply unicast del que tiene la ip al cliente
			imprimirDebug("esArpReply", "ARP Response, hay que ver si es una respuesta del ARP PROBE");
			indice = 28;//posicion comienzo direccion ip origen
//			resultado = TRUE;
			memcpy(&dirIpOrigen, datagrama + indice, 4);
//			dior = ntohl(dirIpOrigen);
			if (dirIpOrigen == DIRECCION_SELECCIONADA.s_addr) {//SI ALGUIEN MANDA UN PROBE CON NUESTRA FUTURA IP
				imprimirDebug("esArpReply", "ARP Response, es una respuesta valida de ARP PROBE");
				resultado = TRUE;
			}
		}
//		else if (datagrama[21] == 0x01) {//es un request de otro cliente, hay que mirar su ip
//			imprimirDebug("esArpReply", "ARP Request, hay que ver si es de la IP objetivo");
//			indice = 38;//posicion donde empieza la direccion ip objetivo
//			memcpy(&dirIpObjetivo, datagrama + indice, 4);
////			dio = ntohl(dirIpObjetivo);
////			pasarCadenaArpADatos(datagrama, da);//HAY QUE IMPLEMENTAR
//			if (dirIpObjetivo == DIRECCION_SELECCIONADA.s_addr) {//SI ALGUIEN MANDA UN PROBE CON NUESTRA FUTURA IP
//				imprimirDebug("esArpReply", "ARP Request, alguien mas quiere nuestra futura IP");
//				resultado = TRUE;
//			}
//		}
	}
	return resultado;
}

int esDhcp(char* datagramaIp, int longitud){
	int resultado = -1;
	if(longitud >= 28 && datagramaIp[9] == 0x11){//si hay info como para albergar cabeceras ip y udp & el proto en cabecera ip es 17 (0x11) => UDP
			if(datagramaIp[20] == 0 && datagramaIp[21] == 67 && datagramaIp[22] == 0 && datagramaIp[23] == 68){//si concuerda con cabec udp
				resultado = 0;
			}
	}
	return resultado;
}

int esUdp(char* datagramaIp, int longitud){
	int resultado = -1;
	if(longitud >= 20 && datagramaIp[9] == 0x11){ // UDP
		resultado = 0;
	}
	return resultado;
}

int esOffer(struct DatosDhcp* dhcp){
	int resultado = -1;
	int pos = 0;
	char actual;
	//se comprueba que es un DHCPOFFER
	if(((u_int8_t)dhcp->options[0]) == 0x63 && ((u_int8_t)dhcp->options[1]) == 0x82 && ((u_int8_t)dhcp->options[2]) == 0x53 && ((u_int8_t)dhcp->options[3]) == 0x63){
		pos += 4;
		while(pos < dhcp->longitudCampoOpciones && resultado == -1){
			actual = dhcp->options[pos];
			imprimirDebug("esOffer","actual%d\n",actual);
			if(actual == 53){ // Tipo de mensaje
				// es Offer
				if(dhcp->options[pos+1] == 1 && dhcp->options[pos+2] == 02) {//1: longitud, 2: DHCPOFFER
					resultado = 0;
				}
			}
			// Se salta el resto de la opción
			else {
				pos += dhcp->options[pos+1]+2;
			}
		}
	}
	else {
		imprimirDebug("esOffer", "Magic cookie incorrecta");
	}
	return resultado;
}


void esperar(int microsegundos){
	unsigned long time = microsegundos * 1000;
	usleep(time);
	time++;
}

int estaAsignada(){
	int arpReplyEntrantes, resultado;
	//crear un nuevo mensaje ARP
	arpReplyEntrantes = enviarArp();
//	arpReplyEntrantes = obtenerArpReply();
	//mandarlo con la direccion ip que estamos a punto de ponernos
	//si recibo un ARP reply => alguien tiene esa ip => devuelve TRUE
	if (arpReplyEntrantes == 1) {
		resultado = TRUE;
	}
	//si no recibo ARP reply => no la tiene nadie, la puedo usar => devuelve FALSE
	else {
		resultado = FALSE;
	}

	return resultado;
}

void establecerInfoMensaje(struct DatosDhcp listaDhcpOffers[]) {
	u_int selector;
	int p, i, len, tamOpciones, limite;
	u_int32_t aux32;
	u_int32_t aux;
	DIRECCION_SELECCIONADA.s_addr = ntohl(listaDhcpOffers[0].yiaddr);//toma la primera direccion que recibe, que corresponde con el primer DHCPOFFER
	DIRECCION_SERVIDOR.s_addr = ntohl(listaDhcpOffers[0].siaddr);//direccion del servidor que envio el primer DHCPOFFER recibido
	obtenerOpcionesServidor(listaDhcpOffers[0]);//creo q sobra
	p = 4;
	limite = (listaDhcpOffers[0].longitudCampoOpciones) - 2;
	imprimirDebug("establecerInfoMensaje", "l%d", limite);
	while (p < limite) {
		selector = listaDhcpOffers[0].options[p++];//empieza a mirar justo despues de la magic cookie(pos=4, ojo al postincremento)
		imprimirDebug("establecerInfoMensaje", "%u \t %d", selector, p);
		switch (selector) {
		case 0:
			imprimirDebug("establecerInfoMensaje", "paso por end?");//mirando el padding
			p = limite + 1;//lleva al final
			break;
		case 1:
			imprimirDebug("establecerInfoMensaje", "Mascara Red");

			if (MASCARA_RED != NULL) {
				free(MASCARA_RED);
			}//actualiza la mascara de red
			MASCARA_RED = malloc(sizeof(struct sockaddr));
			bzero(MASCARA_RED, sizeof(struct sockaddr_in));
			len = listaDhcpOffers[0].options[p++];//saca lo que hay en p=5 (ojo al postincremento)
			i = 0;
			aux = 0;
			while (i < len) {
				aux = aux << 8;
				aux += (u_int8_t)listaDhcpOffers[0].options[p++];//mete la dir de byte en byte de derecha a izquierda en aux
				i++;
			}
			MASCARA_RED->sin_addr.s_addr = ntohl(aux);//guarda la mascara de red
			break;
		case 3:
			if (LISTA_ROUTERS != NULL) {
				free(LISTA_ROUTERS);
			}
			TAM_LISTA_ROUTERS = listaDhcpOffers[0].options[p++] / 4;
			tamOpciones = sizeof(struct in_addr);
			LISTA_ROUTERS = malloc(tamOpciones * TAM_LISTA_ROUTERS);
			i = 0;
			while (i < TAM_LISTA_ROUTERS) {
				memcpy(&aux32, &listaDhcpOffers[0].options[p + (i * tamOpciones)], tamOpciones);
				LISTA_ROUTERS[i * tamOpciones].s_addr = aux32;
				p += tamOpciones;
				i++;
			}
			break;
		case 6:
			if (LISTA_DNS != NULL) {
				free(LISTA_DNS);
			}

			TAM_LISTA_DNS = listaDhcpOffers[0].options[p++] / 4;
			tamOpciones = sizeof(struct in_addr);
			LISTA_DNS = malloc(tamOpciones * TAM_LISTA_DNS);
			i = 0;
			while (i < TAM_LISTA_DNS) {
				memcpy(&aux32, &listaDhcpOffers[0].options[p], tamOpciones);
				imprimirDebug("establecerInfoMensaje", "domain: %u", ntohl(aux32));
				LISTA_DNS[i].s_addr = aux32;
				p += tamOpciones;
				i++;
			}
			break;
		case 12:
			if (NOMBRE_SERVIDOR != NULL) {
				free(NOMBRE_SERVIDOR);
			}

			len = listaDhcpOffers[0].options[p++]+1;
			imprimirDebug("establecerInfoMensaje", "Server hostname length: %d", len);
			NOMBRE_SERVIDOR = malloc(len);
			i = 0;
			while (i < len-1) {
				NOMBRE_SERVIDOR[i] = listaDhcpOffers[0].options[p++];
				i++;
			}
			NOMBRE_SERVIDOR[i] = '\0';
			break;
		case 15:
			if (NOMBRE_DOMINIO != NULL) {
				free(NOMBRE_DOMINIO);
			}

			len = listaDhcpOffers[0].options[p++]+1;
			NOMBRE_DOMINIO = malloc(len);
			i = 0;
			while (i < len-1) {
				NOMBRE_DOMINIO[i] = listaDhcpOffers[0].options[p++];
				i++;
			}
			NOMBRE_DOMINIO[i] = '\0';
			break;
		case 51:
			len = listaDhcpOffers[0].options[p++];
			aux32 = 0;
			memcpy(&aux32, &listaDhcpOffers[0].options[p], 4);
			p+=len;
			PARAMETRO_LEASE = ntohl(aux32);
			break;
		case 54:
			len = listaDhcpOffers[0].options[p++];
			memcpy(&aux32, &listaDhcpOffers[0].options[p], 4);
			if(DIRECCION_SERVIDOR.s_addr == 0){
				DIRECCION_SERVIDOR.s_addr = aux32;
			}
			p += len;
			break;
		case 0xff:
			imprimirDebug("establecerInfoMensaje", "paso por end");
			p = limite + 1;
			break;

		default:
			// Se ignora el campo
			len = listaDhcpOffers[0].options[p++];
			p += len;
			break;
		}
	}
}

// Establece la dirección ip del intefaz.
int establecerIpDispositivo() {
	int test_sock = 0;
	struct sockaddr_in* ip_addr = NULL;
	struct ifreq ifr;

	// Limpia la estructura
	memset(&ifr, 0, sizeof(struct ifreq));

	// Establece la ip del cliente
	ip_addr = (struct sockaddr_in *) &(ifr.ifr_addr);
	bzero(ip_addr, sizeof(struct sockaddr_in));
	ip_addr->sin_family = AF_INET;
	ip_addr->sin_addr.s_addr = DIRECCION_SELECCIONADA.s_addr;

	test_sock = socket(PF_INET,SOCK_DGRAM, 0);
	if (test_sock == -1) {
		perror("socket");
		return (-1);
	}

	// Establece el nombre del dispositivo
	strncpy(ifr.ifr_name, PARAMETRO_IFACE, IFNAMSIZ);
	if (ioctl(test_sock, SIOCSIFADDR, &ifr) != 0) {
		perror("ioctl");
		close(test_sock);
		return (-1);
	} else {
		imprimirDebug("establecerIpDispositivo", "IP address of '%s' set to '%s'",
				PARAMETRO_IFACE, inet_ntoa(DIRECCION_SELECCIONADA));
	}
	close(test_sock);
	return (0);
}

// Establece la máscara de subred del interfaz.
int establecerMascaraDispositivo() {
	int test_sock = 0;
	struct sockaddr_in* netmask = NULL;
	struct ifreq ifr;

	// Limpia la estructura
	memset(&ifr, 0, sizeof(struct ifreq));

	// Establece la máscara de red
	netmask = (struct sockaddr_in *) &(ifr.ifr_netmask);
	bzero(netmask, sizeof(struct sockaddr_in));
	netmask->sin_family = AF_INET;
	netmask->sin_addr.s_addr = MASCARA_RED->sin_addr.s_addr;

	test_sock = socket(PF_INET,SOCK_DGRAM, 0);
	if (test_sock == -1) {
		perror("socket");
		return (-1);
	}

	// Establece el nombre del dispositivo
	strncpy(ifr.ifr_name, PARAMETRO_IFACE, IFNAMSIZ);
	if (ioctl(test_sock, SIOCSIFNETMASK, &ifr) != 0) {
		perror("ioctl");
		close(test_sock);
		return (-1);
	} else {
		imprimirDebug("establecerMascaraDispositivo", "netmask of '%s' set to '%s'",
				PARAMETRO_IFACE, inet_ntoa(MASCARA_RED->sin_addr));
	}
	close(test_sock);
	return (0);
}

// Establece la puerta de enlace del interfaz.
int establecerRouterDispositivo() {
	struct rtentry route;
	int test_sock = 0;
	struct sockaddr_in singw, sindst;

	// Limpia las estructuras
	memset(&singw, 0, sizeof(struct sockaddr));
	memset(&sindst, 0, sizeof(struct sockaddr));
	memset(&route, 0, sizeof(struct rtentry));

	// Establecemos los parámetros del gateway
	singw.sin_family = AF_INET;
	singw.sin_addr.s_addr = LISTA_ROUTERS[0].s_addr;
	sindst.sin_family = AF_INET;
	sindst.sin_addr.s_addr = INADDR_ANY;

	// Rellenamos la ruta
	route.rt_dst = *(struct sockaddr *) &sindst;
	route.rt_gateway = *(struct sockaddr *) &singw;
	route.rt_flags = RTF_GATEWAY;
	route.rt_dev = PARAMETRO_IFACE;

	test_sock = socket(AF_INET,SOCK_DGRAM, 0);
	if (test_sock < 0) {
		perror("socket");
		return (-1);
	}

	// Se borran la misma entrada primero.
	ioctl(test_sock, SIOCDELRT, &route);
	if (ioctl(test_sock, SIOCADDRT, &route) < 0) {
		perror("ioctl");
		close(test_sock);
		return (-1);
	}else{
		imprimirDebug("establecerRouterDispositivo", "route d %s\tp %s\ti %s ", "0.0.0.0", inet_ntoa(singw.sin_addr), PARAMETRO_IFACE);
	}

	close(test_sock);
	return (0);
}

void imprimirDebug(char* method, const char *fmt, ...) {
	char *buf;
	char *timestamp;

	buf = malloc(1024);
	va_list ap;
	va_start (ap, fmt);
	vsprintf(buf, fmt, ap);
	va_end (ap);

	timestamp = obtenerMarcaTiempo();
	if (PARAMETRO_DEBUG == TRUE) {
		printf("-[%s] {%s} %s\n", timestamp, method, buf);
	}
	free(timestamp);
	free(buf);
}

void imprimirTraza(int xid, enum mensajeDhcp estado, char* cadena) {
	char *marcaTiempo;
	char *aux;
	marcaTiempo = obtenerMarcaTiempo();

	if (xid == -1) {

	} else if (xid == -2) {

	} else if (xid == -3) {

	} else {
		switch (estado) {
		case DHCPDISCOVER:
			fprintf(stdout,"#[%s] (%u) DHCPDISCOVER sent.\n", marcaTiempo, xid);
			break;
		case DHCPOFFER:
			fprintf(stdout,"#[%s] (%u) DHCPOFFER received from %s.\n",
					marcaTiempo, xid, cadena);
			break;
		case DHCPREQUEST:
			fprintf(stdout,"#[%s] (%u) DHCPREQUEST sent to %s.\n", marcaTiempo,
					xid, cadena);
			break;
		case DHCPACK:
			fprintf(stdout,"#[%s] (%u) DHCPACK received: %s.\n", marcaTiempo,
					xid, cadena);
			break;
		case DHCPNAK:
			fprintf(stdout,"#[%s] (%u) DHCPNACK received.\n", marcaTiempo, xid);
			break;
		case DHCPDECLINE:
			fprintf(stdout,"#[%s] (%u) DHCPDECLINE sent %s.\n", marcaTiempo, xid,
					cadena);
			break;
		case DHCPRELEASE:
			fprintf(stdout,"#[%s] (%u) DHCPRELEASE sent %s.\n", marcaTiempo, xid,
					cadena);
			break;
		case PID:
			fprintf(stdout,"#[%s] PID=%d.\n", marcaTiempo, getpid());
			break;
		case IP:
			aux = obtenerTrazaConfiguracionRed();
			fprintf(stdout,"#[%s]%s.\n",marcaTiempo, aux);
			free(aux);
			break;
		case DHCPSIGINT:
			fprintf(stdout,"#[%s] SIGINT received.\n", marcaTiempo);
			break;
		case DHCPSIGUSR2:
			fprintf(stdout,"#[%s] SIGUSR2 received.\n", marcaTiempo);
			break;
//		case ARP:
//			fprintf(stdout,"#[%s] (%u) ARP sent.\n", marcaTiempo, xid);
//			break;
		}
	}
	free(marcaTiempo);
}

int inicializarSocketArp(){
	int resultado = 0;
//	int opcionActivada = 1;

//	descriptorSocketArp = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
	descriptorSocketArp = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
//	setsockopt(descriptorSocketArp, SOL_SOCKET, SO_REUSEADDR, &opcionActivada, sizeof(int));
	if (descriptorSocketArp < 0) {
		perror("FALLO AL CREAR EL SOCKET BAJO NIVEL => funciones.c");
		resultado = -1;
	}
	if (resultado >= 0) {
		//rellenamos la estructura asociada al socket raw, sockaddr_ll
		bzero(&socketArp, sizeof(struct sockaddr_ll));//memset(&socketArp, 0, sizeof(struct sockaddr_ll));//relleno a ceros
		socketArp.sll_family = AF_PACKET;
//		socketArp.sll_protocol = htons(ETH_P_ARP);
		socketArp.sll_protocol = htons(ETH_P_ALL);
		socketArp.sll_ifindex = obtenerIndiceInterfaz();
		if (socketArp.sll_ifindex < 0) {
			resultado = -1;
		}
//		socketArp.sll_hatype = 0XFFFF;
//		socketArp.sll_pkttype = PACKET_BROADCAST;
//		socketArp.sll_halen = 6; //ETHERNET
//		socketArp.sll_addr[0] = 255;
//		socketArp.sll_addr[1] = 255;
//		socketArp.sll_addr[2] = 255;
//		socketArp.sll_addr[3] = 255;
//		socketArp.sll_addr[4] = 255;
//		socketArp.sll_addr[5] = 255;
//		socketArp.sll_addr[6] = 0;
//		socketArp.sll_addr[7] = 0;
	}
	if (resultado >= 0) {//asocio el descriptor de socket con su direccion
		resultado = bind(descriptorSocketArp, (struct sockaddr *) &socketArp, sizeof(struct sockaddr_ll));
		if (resultado < 0) {
			perror("FALLO bind DESCRIPTOR DE SOCKET RAW => funciones.c");
		}
	}
	return resultado;
}

int inicializarSocketNivelEnlace(){
	int resultado = 0;
	int opcionActivada = 1;

	descriptorSocketNivelEnlace = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
	setsockopt(descriptorSocketNivelEnlace, SOL_SOCKET, SO_REUSEADDR, &opcionActivada, sizeof(int));
	if (descriptorSocketNivelEnlace < 0) {
		perror("FALLO AL CREAR EL SOCKET BAJO NIVEL => funciones.c");
		resultado = -1;
	}
	if (resultado >= 0) {
		//rellenamos la estructura asociada al socket raw, sockaddr_ll
		bzero(&socketNivelEnlace, sizeof(struct sockaddr_ll));//memset(&socketNivelEnlace, 0, sizeof(struct sockaddr_ll));//relleno a ceros
		socketNivelEnlace.sll_family = AF_PACKET;
		socketNivelEnlace.sll_protocol = htons(ETH_P_IP);
		socketNivelEnlace.sll_ifindex = obtenerIndiceInterfaz();
		if (socketNivelEnlace.sll_ifindex < 0) {
			resultado = -1;
		}
		socketNivelEnlace.sll_hatype = 0XFFFF;
		socketNivelEnlace.sll_pkttype = PACKET_BROADCAST;
		socketNivelEnlace.sll_halen = 6; //ETHERNET
		socketNivelEnlace.sll_addr[0] = 255;
		socketNivelEnlace.sll_addr[1] = 255;
		socketNivelEnlace.sll_addr[2] = 255;
		socketNivelEnlace.sll_addr[3] = 255;
		socketNivelEnlace.sll_addr[4] = 255;
		socketNivelEnlace.sll_addr[5] = 255;
		socketNivelEnlace.sll_addr[6] = 0;
		socketNivelEnlace.sll_addr[7] = 0;
	}
	if (resultado >= 0) {//asocio el descriptor de socket con su direccion
		resultado = bind(descriptorSocketNivelEnlace, (struct sockaddr *) &socketNivelEnlace, sizeof(struct sockaddr_ll));
		if (resultado < 0) {
			perror("FALLO bind DESCRIPTOR DE SOCKET RAW => funciones.c");
		}
	}
	return resultado;
}

//inicializa los temporizadores
void inicializarTemporizadores() {
	TEMPORIZADOR_ACTUAL = 4;
	TEMPORIZADOR_SEC = 0;
	TEMPORIZADOR_USEC = 0;
}

int levantarInterfaz(const char* interfaz){
	int descriptorSocket;
	struct ifreq ifr;
	int estaLevantada = 0;
	int resultado = 0;

	descriptorSocket = socket(PF_INET, SOCK_DGRAM, 0);
	strcpy(ifr.ifr_name, PARAMETRO_IFACE);//da el nombre que se paso por parametro
	ioctl(descriptorSocket, SIOCGIFFLAGS, &ifr);//obtiene el flag

	estaLevantada = ifr.ifr_ifru.ifru_flags & IFF_UP;

	if(estaLevantada == 0){
		ifr.ifr_ifru.ifru_flags = ifr.ifr_ifru.ifru_flags | IFF_UP;
		ioctl(descriptorSocket, SIOCSIFFLAGS, &ifr);//establece la interfaz como activa
	}
	else{
		resultado = -1;
		fprintf(stderr,"ERROR: %s is already enabled\n", interfaz);
	}
	close(descriptorSocket);
	return resultado;
}

void liberarCadenaDhcp(struct CadenaDhcp *mensaje){
	if (mensaje->longitud > 0) {
		free(mensaje->contenido);
	}
	free(mensaje);
}

void liberarDatosDhcp(struct DatosDhcp *datos){
	if (datos->longitudCampoOpciones > 0){
		free(datos->options);
	}
	free(datos);
}

//int obtenerArpReply() {
//	fd_set conjuntoRecibidos; //conjunto de descriptores de entrada
//	struct timeval umbral; //intervalo maximo de espera para completar la seleccion
//	int resultado;
//	char * buf = malloc(1000);
//	int arp = -1;
//
//	// Se establecen los sets de descriptores
//	FD_ZERO(&conjuntoRecibidos); //inicializa el conjunto de descriptores a un conjunto null
//	FD_SET(descriptorSocketArp, &conjuntoRecibidos); //incluye el descriptorSocketNivelEnlace en el conjunto conjuntoRecibidos de descriptores a ser leidos
//
//	umbral.tv_sec = ANNOUNCE_WAIT;
//	umbral.tv_usec = 0;
//
//	resultado = 1;
//	while (resultado > 0 && arp == -1) {
//		resultado = select(descriptorSocketArp + 1, &conjuntoRecibidos, NULL, NULL, &umbral);//resultado=0 si el limite de tiempo expira
//
//		umbral.tv_sec -= 1;
//
//		imprimirDebug("obtenerArpReply", "resultado %d", resultado);
//		if(resultado < 0) {
//			perror("select");
//		}
//		else if(resultado> 0) {
//			// Lee un buffer de 1000 bytes
//			int bytesRecibidos = recvfrom(descriptorSocketArp, buf, 1000, 0, NULL, NULL);
//			imprimirDebug("obtenerMensajeAck", "recibido %d",bytesRecibidos);
//			arp = esArpReply(buf, bytesRecibidos);//TENGO QUE COMPROBAR REPLY
//			if(arp == TRUE){//
//				//HAY QUE BUSCARSE OTRA DIRECCION IP
//				imprimirDebug("obtenerArpReply", "El paquete es arp\n");
//			}
//			else {
//				imprimirDebug("obtenerArpReply", "El paquete no es arp\n");
//			}
//		}
//	}
//
//	if (arp == 1) {
//		resultado = 1;
//	}
//	else {
//		resultado = 0;
//	}
//	free(buf);
//	return resultado;
//}

int obtenerCabDhcpDesdeMensIp(struct DatosDhcp * dhcp, char * ip_msg, int ip_msg_len) {
	u_int16_t longTotalPaq;
	int tamCabIpUdp = 28;
	struct CadenaDhcp dhcpM;
	int resultado = 0;

	// Obtenemos el tamaño del paquete desde la cabecera ip
	longTotalPaq = (ip_msg[2] << 8) + ip_msg[3];
	imprimirDebug("obtenerCabDhcpDesdeMensIp", "tamaño total del paquete %d\n",longTotalPaq);

	// Comprueba que sea udp y en ese caso que sea dhcp
	if(ip_msg[9] == 0x11){ // UDP
		if(ip_msg[20] == 0 && ip_msg[21] == 67 && ip_msg[22] == 0 && ip_msg[23] == 68){ // DHCP
			dhcpM.contenido = (unsigned char *) ip_msg + tamCabIpUdp;
			dhcpM.longitud = ip_msg_len - tamCabIpUdp;
			imprimirDebug("obtenerCabDhcpDesdeMensIp", "el paquete es dhcp\n");
		}
		else {
			resultado = -1;
		}
	}
	else {
		resultado = -1;
	}

	if(resultado == -1){
		imprimirDebug("obtenerCabDhcpDesdeMensIp", "el paquete no es dhcp\n");
		return -1;
	}

	if(pasarCadenaDhcpADatos(dhcp, &dhcpM) >= 0)
		return longTotalPaq;
	return -1;
}

int obtenerCabeceraEthernet(unsigned char **mensaje, struct CadenaDhcp *cadena) {
	struct ethhdr *cabeceraEthernet;
	int tamTotal, indice;

	tamTotal = TAM_CAB_ETH + cadena->longitud;
	*mensaje = malloc(tamTotal);
	cabeceraEthernet = malloc(sizeof(struct ethhdr));
	bzero(cabeceraEthernet, sizeof(struct ethhdr));

	cabeceraEthernet->h_source[0] = DIRECCION_HW[0];
	cabeceraEthernet->h_source[1] = DIRECCION_HW[1];
	cabeceraEthernet->h_source[2] = DIRECCION_HW[2];
	cabeceraEthernet->h_source[3] = DIRECCION_HW[3];
	cabeceraEthernet->h_source[4] = DIRECCION_HW[4];
	cabeceraEthernet->h_source[5] = DIRECCION_HW[5];
	cabeceraEthernet->h_dest[0] = 0xff;
	cabeceraEthernet->h_dest[1] = 0xff;
	cabeceraEthernet->h_dest[2] = 0xff;
	cabeceraEthernet->h_dest[3] = 0xff;
	cabeceraEthernet->h_dest[4] = 0xff;
	cabeceraEthernet->h_dest[5] = 0xff;
	cabeceraEthernet->h_proto = htons(ETH_P_ARP);

	indice = 0;
	memcpy(*mensaje + indice, &cabeceraEthernet->h_dest, 6);
	indice += 6;
	memcpy(*mensaje + indice, &cabeceraEthernet->h_source, 6);
	indice += 6;
	memcpy(*mensaje + indice, &cabeceraEthernet->h_proto, 2);
	indice += 2;
	memcpy(*mensaje + indice, cadena->contenido, cadena->longitud);

	free(cabeceraEthernet);

	return tamTotal;
}

int obtenerCabeceraIp(unsigned char **mensaje, in_addr_t dirIpDestino, unsigned char *mensajeUdp, int tamUdp){
	int tamTotal, indice;
	u_int8_t caux;
	struct iphdr *cabeceraIp;

	tamTotal = 20 + tamUdp;
	*mensaje = malloc(tamTotal);
	cabeceraIp = malloc(sizeof(struct iphdr));

	//relleno de la cabecera ip
	bzero(cabeceraIp, sizeof(struct iphdr));

	cabeceraIp->version = 4;
	cabeceraIp->ihl = 5;
	cabeceraIp->tos = 0;
	cabeceraIp->tot_len = htons(tamTotal);
	cabeceraIp->id = getuid();
	cabeceraIp->frag_off = 0;
	cabeceraIp->ttl = 64;
	cabeceraIp->protocol = IPPROTO_UDP;
	cabeceraIp->check = 0;
	cabeceraIp->check = calcularChecksum((unsigned short *) cabeceraIp, sizeof(struct iphdr));
	cabeceraIp->saddr = 0;
	cabeceraIp->daddr = dirIpDestino;

	indice = 0;
	//insertamos los campos de 4 bytes (version e ihl) como un byte completo
	caux = cabeceraIp->version;
	caux = caux << 4;
	caux += cabeceraIp->ihl;
	memcpy(*mensaje, &caux, 1);
	indice += 1;
	memcpy(*mensaje + indice, &cabeceraIp->tos, 1);
	indice += 1;
	memcpy(*mensaje + indice, &cabeceraIp->tot_len, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraIp->id, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraIp->frag_off, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraIp->ttl, 1);
	indice += 1;
	memcpy(*mensaje + indice, &cabeceraIp->protocol, 1);
	indice += 1;
	memcpy(*mensaje + indice, &cabeceraIp->check, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraIp->saddr, 4);
	indice += 4;
	memcpy(*mensaje + indice, &cabeceraIp->daddr, 4);
	indice += 4;

	memcpy(*mensaje + indice, mensajeUdp, tamUdp);

	free(cabeceraIp);
	return tamTotal;
}

int obtenerCabeceraUdp(unsigned char **mensaje, struct CadenaDhcp* cadena){
	int tamTotal, indice;
	struct udphdr *cabeceraUdp;

	tamTotal = 8 + cadena->longitud;
	*mensaje = malloc(tamTotal);
	cabeceraUdp = malloc(sizeof(struct udphdr));
	cabeceraUdp->source = htons(PUERTO_DHCP_CLIENTE);
	cabeceraUdp->dest = htons(PUERTO_DHCP_SERVIDOR);
	cabeceraUdp->len = htons(tamTotal);
	cabeceraUdp->check = 0;

	indice = 0;
	memcpy(*mensaje, &cabeceraUdp->source, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraUdp->dest, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraUdp->len, 2);
	indice += 2;
	memcpy(*mensaje + indice, &cabeceraUdp->check, 2);
	indice += 2;
	memcpy(*mensaje + indice, cadena->contenido, cadena->longitud);

	free(cabeceraUdp);

	return tamTotal;
}

int obtenerTamTotal(unsigned char **mensaje, struct CadenaDhcp* cadena){
	int tamTotal;
	int indice;

	tamTotal = cadena->longitud;
	*mensaje = malloc(tamTotal);

	indice = 0;
	memcpy(*mensaje + indice, cadena->contenido, cadena->longitud);

	return tamTotal;
}

void obtenerDireccionHW(){
	int descriptorSocket;
	struct ifreq ifr;

	descriptorSocket = socket(PF_INET, SOCK_DGRAM, 0);

	strcpy(ifr.ifr_name, PARAMETRO_IFACE);
	ioctl(descriptorSocket, SIOCGIFHWADDR, &ifr);//consigo la dir hw del dispositivo
	if (DIRECCION_HW == NULL) {
		DIRECCION_HW = malloc(TAM_DIRECCION_HW);//reservo la memoria para la dir hw
	}
	bzero(DIRECCION_HW, TAM_DIRECCION_HW);
	memcpy(DIRECCION_HW, ifr.ifr_hwaddr.sa_data, TAM_DIRECCION_HW);//asigno la dir
	close(descriptorSocket);
}
// Devuelve el tamaño del paquete ip contenido dentro del buffer pasado
int obtenerLongPaqIp( char* buf, int len){
	int ret = -1;
	if(len >= 20){
		// Obtenemos el tamaño del paquete desde la cabecera ip
		ret = (buf[2] << 8) + buf[3];
	}
	return ret;
}

int obtenerIndiceInterfaz(){
	int resultado;
	int descriptorSocket;
	struct ifreq ifr;
	descriptorSocket = socket(PF_INET, SOCK_DGRAM, 0);
	strcpy(ifr.ifr_ifrn.ifrn_name, PARAMETRO_IFACE);

	if (ioctl(descriptorSocket, SIOCGIFINDEX, &ifr)) {
		perror("FALLO AL ACCEDER AL INDICE DE INTERFAZ => funciones.c");
		fprintf(stderr, "ERROR: La interfaz especificada no es válida.\n");
		return -1;
	}
	resultado = ifr.ifr_ifru.ifru_ivalue;

	return resultado;
}

char * obtenerMarcaTiempo(){
	char* marcaTiempo;
	time_t t;
	struct tm local, gtm;
	struct tm *tmp;
	int hora, minuto;

	marcaTiempo = malloc(50);
	t = time(NULL);//asigna la fecha/hora actual
	tmp = localtime(&t);//devuelve la fecha/hora local
	memcpy(&local, tmp, sizeof(struct tm));

	tmp = gmtime(&t);//devuelve la fecha/hora UTC
	memcpy(&gtm, tmp, sizeof(struct tm));
	hora = (int) local.tm_gmtoff;//diferencia horario con respecto a UTC(negativo o positivo)
	minuto = (hora / 60) % 60;
	hora = hora / 3600;
	if (hora >= 0)
		strftime(marcaTiempo, 50, "%Y-%m-%d %H:%M:%S+", &local);
	else
		strftime(marcaTiempo, 50, "%Y-%m-%d %H:%M:%S-", &local);

	sprintf(marcaTiempo, "%s%.2d:%.2d", marcaTiempo, hora, minuto);
	return marcaTiempo;
}

// Obtiene el mensaje ACK o NACK del servidor
// 1 en caso de ACK, 0 en caso de NACK, -1 en caso de error
int obtenerMensajeAck() {
	fd_set recvset; //conjunto de descriptores de entrada
	struct timeval tv, inicio, fin; //intervalo maximo de espera para completar la seleccion
	int resultado = 1;
	char * buf = malloc(1000);
	struct DatosDhcp dhcpRecibido;
	int ack = -1;
	int tamanyoPaquete;
	char tempmsg[50];

	// Se establecen los sets de descriptores
	FD_ZERO(&recvset); //inicializa el conjunto de descriptores a un conjunto null
	FD_SET(descriptorSocketNivelEnlace, &recvset); //incluye el descriptorSocketNivelEnlace en el conjunto recvset de descriptores a ser leidos

	obtenerSiguienteTemporizador(&tv);
	imprimirDebug("obtenerMensajeAck", "Timeout sec:%d, usec:%d", tv.tv_sec,
			tv.tv_usec);
	gettimeofday(&inicio, NULL);

	//Recibimos multiples respuestas
	resultado = 1;
	while (resultado > 0 && ack == -1) {
		resultado = select(descriptorSocketNivelEnlace + 1, &recvset, NULL, NULL, &tv);//resultado=0 si el limite de tiempo expira
		gettimeofday(&fin, NULL);
		reducirTemporizador(&tv, &inicio, &fin);
		imprimirDebug("obtenerMensajeAck", "Timeout sec:%d, usec:%d", tv.tv_sec, tv.tv_usec);
		imprimirDebug("obtenerMensajeAck", "resultado %d", resultado);
		if(resultado < 0) {
			perror("select");
		} else if(resultado> 0) {

			// Lee un buffer de 1000 bytes
			int recv_size = recvfrom(descriptorSocketNivelEnlace, buf, 1000, 0, NULL, NULL);
			imprimirDebug("obtenerMensajeAck", "recibido %d",recv_size);

			tamanyoPaquete = obtenerLongPaqIp(buf, recv_size);//
			if(esUdp(buf, recv_size) == 0){//
				if(esDhcp(buf, recv_size) == 0){//
					if(obtenerCabDhcpDesdeMensIp(&dhcpRecibido, buf, recv_size) > 0){//obtenerCabDhcp
						// Se comprueba que el mensaje responda al ultimo Xid
						if(dhcpRecibido.xid == XID) {
							ack = esAck(&dhcpRecibido);//ack=1 => DHCPACK, ack=0 => recibido DHCPNAK
							if(ack == -1){
								imprimirDebug("obtenerMensajeAck", "El paquete ack no es válido\n");
							}

							imprimirDebug("obtenerMensajeAck", "ipOrigen %d",dhcpRecibido.siaddr);
							imprimirDebug("obtenerMensajeAck", "id %d",dhcpRecibido.xid);
						} else {
							imprimirDebug("obtenerMensajeAck", "Distinto Xid");
						}

						if(dhcpRecibido.longitudCampoOpciones> 0) {
							free(dhcpRecibido.options);
						}
					}
				}
				else {
					imprimirDebug("obtenerMensajeAck", "El paquete no es dhcp\n");
				}
			}
			else {
				imprimirDebug("obtenerMensajeAck", "El paquete no es udp\n");
			}
		}
	}
	bzero(tempmsg, 50);
	if(ack == 0) {
		imprimirTraza(dhcpRecibido.xid, DHCPNAK, NULL);
	}
	else if(ack == 1) {
		if(PARAMETRO_LEASE == 0xffffffff) {
			sprintf(tempmsg, "%s with leasing inf seconds",inet_ntoa(DIRECCION_SELECCIONADA));
		}else{
			sprintf(tempmsg, "%s with leasing %u seconds", inet_ntoa(DIRECCION_SELECCIONADA), PARAMETRO_LEASE);
		}
		imprimirTraza(dhcpRecibido.xid, DHCPACK, tempmsg);
	}
	resultado = ack;
	if((ack =! -1) && (dhcpRecibido.longitudCampoOpciones> 0)){
		imprimirDebug("obtenerMensajeAck", "free options");
		free(dhcpRecibido.options);
	}
	imprimirDebug("obtenerMensajeAck", "free buf");
	free(buf);
	imprimirDebug("obtenerMensajeAck", "fin");
	return resultado;
}

// Obtiene el mensaje ACK o NACK del servidor
// 1 en caso de ACK, 0 en caso de NACK, -1 en caso de error
int obtenerMensajeAckRebinding() {
	fd_set recvset; //conjunto de descriptores de entrada
//	struct timeval tv, inicio, fin; //intervalo maximo de espera para completar la seleccion
	struct timeval tv; //intervalo maximo de espera para completar la seleccion
	int resultado = 1;
	char * buf = malloc(1000);
	struct DatosDhcp dhcpRecibido;
	int ack = -1;
	int tamanyoPaquete;
	char tempmsg[50];

	// Se establecen los sets de descriptores
	FD_ZERO(&recvset); //inicializa el conjunto de descriptores a un conjunto null
	//	FD_SET(descriptorSocketNivelEnlace, &recvset); //incluye el descriptorSocketNivelEnlace en el conjunto recvset de descriptores a ser leidos
	FD_SET(descriptorSocketDatagramasUDP, &recvset); //incluye el descriptorSocketNivelEnlace en el conjunto recvset de descriptores a ser leidos

	tv.tv_sec = 0.125 * PARAMETRO_LEASE;
	tv.tv_usec = 0;

//	obtenerSiguienteTemporizador(&tv);
	imprimirDebug("obtenerMensajeAckRebinding", "Timeout sec:%d, usec:%d", tv.tv_sec,
			tv.tv_usec);
//	gettimeofday(&inicio, NULL);

	//Recibimos multiples respuestas
	resultado = 1;
	while (resultado > 0 && ack == -1) {
//		resultado = select(descriptorSocketNivelEnlace + 1, &recvset, NULL, NULL, &tv);//resultado=0 si el limite de tiempo expira
		resultado = select(descriptorSocketDatagramasUDP + 1, &recvset, NULL, NULL, &tv);//resultado=0 si el limite de tiempo expira
//		gettimeofday(&fin, NULL);
//		reducirTemporizador(&tv, &inicio, &fin);
		imprimirDebug("obtenerMensajeAckRebinding", "Timeout sec:%d, usec:%d", tv.tv_sec, tv.tv_usec);
		imprimirDebug("obtenerMensajeAckRebinding", "resultado %d", resultado);
		if(resultado < 0) {
			perror("select");
		} else if(resultado> 0) {

			// Lee un buffer de 1000 bytes
//			int recv_size = recvfrom(descriptorSocketNivelEnlace, buf, 1000, 0, NULL, NULL);
			int recv_size = recvfrom(descriptorSocketDatagramasUDP, buf, 1000, 0, NULL, NULL);
			imprimirDebug("obtenerMensajeAckRebinding", "recibido %d",recv_size);

			tamanyoPaquete = obtenerLongPaqIp(buf, recv_size);//
			if(esUdp(buf, recv_size) == 0){//
				if(esDhcp(buf, recv_size) == 0){//
					if(obtenerCabDhcpDesdeMensIp(&dhcpRecibido, buf, recv_size) > 0){//obtenerCabDhcp
						// Se comprueba que el mensaje responda al ultimo Xid
						if(dhcpRecibido.xid == XID) {
							ack = esAck(&dhcpRecibido);//ack=1 => DHCPACK, ack=0 => recibido DHCPNAK
							if(ack == -1){
								imprimirDebug("obtenerMensajeAckRebinding", "El paquete ack no es válido\n");
							}

							imprimirDebug("obtenerMensajeAckRebinding", "ipOrigen %d",dhcpRecibido.siaddr);
							imprimirDebug("obtenerMensajeAckRebinding", "id %d",dhcpRecibido.xid);
						} else {
							imprimirDebug("obtenerMensajeAckRebinding", "Distinto Xid");
						}

						if(dhcpRecibido.longitudCampoOpciones> 0) {
							free(dhcpRecibido.options);
						}
					}
				}
				else {
					imprimirDebug("obtenerMensajeAckRebinding", "El paquete no es dhcp\n");
				}
			}
			else {
				imprimirDebug("obtenerMensajeAckRebinding", "El paquete no es udp\n");
			}
		}
	}
	bzero(tempmsg, 50);
	if(ack == 0) {
		imprimirTraza(dhcpRecibido.xid, DHCPNAK, NULL);
	}
	else if(ack == 1) {
		if(PARAMETRO_LEASE == 0xffffffff) {
			sprintf(tempmsg, "%s with leasing inf seconds",inet_ntoa(DIRECCION_SELECCIONADA));
		}else{
			sprintf(tempmsg, "%s with leasing %u seconds", inet_ntoa(DIRECCION_SELECCIONADA), PARAMETRO_LEASE);
		}
		imprimirTraza(dhcpRecibido.xid, DHCPACK, tempmsg);
	}
	resultado = ack;
	if((ack =! -1) && (dhcpRecibido.longitudCampoOpciones> 0)){
		imprimirDebug("obtenerMensajeAckRebinding", "free options");
		free(dhcpRecibido.options);
	}
	imprimirDebug("obtenerMensajeAckRebinding", "free buf");
	free(buf);
	imprimirDebug("obtenerMensajeAckRebinding", "fin");
	return resultado;
}

int obtenerMensajeArp(unsigned char** mensaje, struct DatosArp * datos) {
	int tamTotal;
	struct CadenaDhcp* cadena;//mensaje completo con todas las cabeceras, no sólo datos dhcp

	tamTotal = 0;
	cadena = pasarDatosArpACadena(datos);
	tamTotal = obtenerCabeceraEthernet(mensaje, cadena);
//	tamTotal = obtenerTamTotal(mensaje, cadena);

	liberarCadenaDhcp(cadena);
	return tamTotal;
}

int obtenerMensajeEthernet(unsigned char** mensaje, in_addr_t nombreHost, struct DatosDhcp * datos) {
	int tamTotal;
	struct CadenaDhcp* cadena;//mensaje completo con todas las cabeceras, no sólo datos dhcp
	unsigned char** mensajeUdp;

	mensajeUdp = malloc(4);//?

	tamTotal = 0;
	cadena = pasarDatosDhcpACadena(datos);
	tamTotal = obtenerCabeceraUdp(mensajeUdp, cadena);//mete en mensajeUdp la cabecera UDP y a continuacion la cadena DHCP
	tamTotal = obtenerCabeceraIp(mensaje, nombreHost, *mensajeUdp, tamTotal);//mete en mensaje la cabecera IP y despues mensajeUdp

	free(*mensajeUdp);
	free(mensajeUdp);
	liberarCadenaDhcp(cadena);
	return tamTotal;
}

int obtenerOffers(struct DatosDhcp messages[]) {
	fd_set recibidos;
	struct timeval tv, inicio, fin;
	int resultado = 1;
	char * buf = malloc(1000);
	int numeroDhcps = 0;
	char * msg_string;
	char *str_serv_addr;
	char *str_ip_addr;
	struct in_addr serv_addr_temp;
	struct in_addr ip_addr_temp;
	char * straux;

	// Se establecen los conjuntos de descriptores
	FD_ZERO(&recibidos);
	FD_SET(descriptorSocketNivelEnlace, &recibidos);

	//Recibimos multiples respuestas
	resultado = 1;

	// Tiempo de espera del select - Hay que hacerlo en cada iteración del bucle
	obtenerSiguienteTemporizador(&tv);
	imprimirDebug("obtenerOffers", "Timeout sec:%d, usec:%d", tv.tv_sec,
			tv.tv_usec);
	gettimeofday(&inicio, NULL);

	while (resultado > 0 && numeroDhcps < NUMERO_MAXIMO_DHCPOFFERS) {
		resultado = select(descriptorSocketNivelEnlace + 1, &recibidos, NULL, NULL, &tv);
		gettimeofday(&fin, NULL);
		reducirTemporizador(&tv, &inicio, &fin);
		imprimirDebug("obtenerOffers", "Timeout sec:%d, usec:%d", tv.tv_sec,tv.tv_usec);

		if(resultado < 0) {
			perror("select, funciones.c");
			imprimirDebug("obtenerOffers", "Fin timeout?");
		} else if(resultado> 0) {
			int bytesRecibidos = recvfrom(descriptorSocketNivelEnlace, buf, 1000, 0, NULL, NULL);

			if(obtenerCabDhcpDesdeMensIp(&messages[numeroDhcps], buf, bytesRecibidos) >= 0){

				// Se comprueba que el mensaje responda al ultimo Xid y que tenga el mismo chaddr
				if(messages[numeroDhcps].xid == XID && compararDirHw(messages[numeroDhcps].chaddr)) {
					if(esOffer(&messages[numeroDhcps]) == 0){
						msg_string = malloc(60);

						ip_addr_temp.s_addr = ntohl(messages[numeroDhcps].yiaddr);
						serv_addr_temp.s_addr = ntohl(messages[numeroDhcps].siaddr);
						if(serv_addr_temp.s_addr==0){
							serv_addr_temp.s_addr = obtenerOpcionesServidor(messages[numeroDhcps]);
						}
						straux = inet_ntoa(serv_addr_temp);
						str_serv_addr = malloc(strlen(straux)+1);
						bzero(str_serv_addr, strlen(straux)+1);
						memcpy(str_serv_addr, straux, strlen(straux));
						straux = inet_ntoa(ip_addr_temp);
						str_ip_addr = malloc(strlen(straux)+1);
						bzero(str_ip_addr, strlen(straux)+1);
						memcpy(str_ip_addr, straux, strlen(straux));

						sprintf(msg_string, "%s (offered %s)",str_serv_addr, str_ip_addr);

						imprimirTraza(messages[numeroDhcps].xid, DHCPOFFER, msg_string);

						free(msg_string);
						free(str_serv_addr);
						free(str_ip_addr);

						imprimirDebug("obtenerOffers", "ipOrigen %d",messages[numeroDhcps].siaddr);
						imprimirDebug("obtenerOffers", "id %d",messages[numeroDhcps].xid);

						numeroDhcps++;
					}else{
						imprimirDebug("obtenerOffers", "Mensaje no DHCP Offer");
					}
				}else{
					imprimirDebug("obtenerOffers", "Distinto Xid %d o chaddr %s", messages[numeroDhcps].xid, messages[numeroDhcps].chaddr);
					if(messages[numeroDhcps].longitudCampoOpciones> 0)
						free(messages[numeroDhcps].options);
				}
			}else imprimirDebug("obtenerOffers", "El mensaje no es dhcp\n");
		}
	}
	resultado = numeroDhcps;
	free(buf);
	return resultado;
}

int obtenerOpcionesServidor(struct DatosDhcp ip) {
	u_int case_;
	int p, h, lenght;
	u_int32_t aux32;

	p = 4;
	lenght = (ip.longitudCampoOpciones) - 2;
	imprimirDebug("obtenerOpcionesServidor", "l%d", lenght);
	while (p < lenght) {
		case_ = ip.options[p++];
		imprimirDebug("obtenerOpcionesServidor", "%u \t %d", case_, p);
		switch (case_) {
			case 0:
			imprimirDebug("obtenerOpcionesServidor", "paso por end?");
			p = lenght +1;
			break;
		case 54:
			h = ip.options[p++];
			memcpy(&aux32, &ip.options[p], 4);
			p += h;
			break;
		case 0xff:
			imprimirDebug("obtenerOpcionesServidor", "paso por end");
			p = lenght +1;
			break;
		default:
			// Se ignora el campo
			h = ip.options[p++];
			p += h;
			break;
		}
	}
	return aux32;
}

void obtenerSiguienteTemporizador(struct timeval *tv){
	double  r;
	r = (((double)rand()/(double)RAND_MAX)*2)-1;
	TEMPORIZADOR_SEC = (TEMPORIZADOR_ACTUAL + r);
	TEMPORIZADOR_USEC = ((TEMPORIZADOR_ACTUAL + r) - TEMPORIZADOR_SEC) *1000000;
	imprimirDebug("obtenerSiguienteTemporizador","r %e, AT %d, ST %d, UT %d",r, TEMPORIZADOR_ACTUAL, TEMPORIZADOR_SEC, TEMPORIZADOR_USEC);
	tv->tv_sec = TEMPORIZADOR_SEC;
	tv->tv_usec = TEMPORIZADOR_USEC;
	TEMPORIZADOR_ACTUAL *=2;
}

int obtenerTamOpcionesDecline(char ** opciones){
	int indice, tamOpciones;
	u_int8_t magicCookie[4];
	u_int8_t tipoMensaje[3];
	u_int8_t direccionPedida[6]; //50
	u_int8_t identificacionServidor[6]; //54
	u_int8_t finOpciones;

	magicCookie[0] = 99;
	magicCookie[1] = 130;
	magicCookie[2] = 83;
	magicCookie[3] = 99;

	tipoMensaje[0] = 0x35;
	tipoMensaje[1] = 0x1;
	tipoMensaje[2] = 0x4;//DHCPDECLINE=4

	imprimirDebug("obtenerTamOpcionesDecline", "DIRECCION_SELECCIONADA");
	direccionPedida[0] = 50;
	direccionPedida[1] = 4;
	memcpy(&direccionPedida[2], &DIRECCION_SELECCIONADA.s_addr, 4);

	imprimirDebug("obtenerTamOpcionesDecline", "DIRECCION_SERVIDOR");
	identificacionServidor[0] = 0x36;
	identificacionServidor[1] = 0x4;
	memcpy(&identificacionServidor[2], &DIRECCION_SERVIDOR, 4);

	finOpciones = 0xFF;//255: codigo de fin de opciones

	tamOpciones = 64;
	*opciones = malloc(tamOpciones);

	bzero(*opciones, tamOpciones);
	indice = 0;
	memcpy(*opciones, magicCookie, 4);//mete en opciones la magic cookie
	indice += 4;//actualiza el indice a la posicion de siguiente insercion
	memcpy(*opciones + indice, tipoMensaje, 3);//mete el tipo de mensaje
	indice += 3;//actualiza indice posicion
	memcpy(*opciones + indice, direccionPedida, 6);
	indice += 6;

	memcpy(*opciones + indice, identificacionServidor, 6);
	indice += 6;

	memcpy(*opciones + indice, &finOpciones, 1);
	indice += 1;

	return tamOpciones;
}


int obtenerTamOpcionesDiscover(char ** opciones){
	int indice, tamOpciones;
	u_int32_t alquiler;
	u_int8_t magicCookie[4];
	u_int8_t tipoMensaje[3];
	u_int8_t direccionPedida[6]; //50
	u_int8_t nombreHost[2]; //12
	int longitudNombreHost;
	u_int8_t opcionLease[6];
	u_int8_t finOpciones;

	alquiler = PARAMETRO_LEASE;

	magicCookie[0] = 99;
	magicCookie[1] = 130;
	magicCookie[2] = 83;
	magicCookie[3] = 99;

	tipoMensaje[0] = 0x35;
	tipoMensaje[1] = 0x1;
	tipoMensaje[2] = 0x1;

	if(PARAMETRO_ADDRESS != NULL){
		imprimirDebug("obtenerTamOpcionesDiscover", "PARAMETRO_ADDRESS");
		direccionPedida[0] = 50;
		direccionPedida[1] = 4;
		memcpy(&direccionPedida[2], &PARAMETRO_ADDRESS->s_addr, 4);
	}

	if(PARAMETRO_HOSTNAME != NULL){
		longitudNombreHost = strlen(PARAMETRO_HOSTNAME);
		imprimirDebug("obtenerTamOpcionesDiscover", "PARAMETRO_HOSTNAME %d", longitudNombreHost);
		nombreHost[0] = 12;
		nombreHost[1] = longitudNombreHost;
	}

	opcionLease[0] = 51;//codigo de la opcion 51: IP address lease time
	opcionLease[1] = 4;//longitud de las opciones de lease time
	opcionLease[5] = alquiler;//guarda el byte menos significativo de PARAMETRO_LEASE
	alquiler = alquiler >> 8;//desplaza 8 bytes a la derecha
	opcionLease[4] = alquiler;//guarda el byte menos significativo
	alquiler = alquiler >> 8;
	opcionLease[3] = alquiler;//guarda el byte menos significativo
	alquiler = alquiler >> 8;
	opcionLease[2] = alquiler;//guarda el byte menos significativo

	finOpciones = 0xFF;//255: codigo de fin de opciones

	tamOpciones = 64;
	*opciones = malloc(tamOpciones);

	bzero(*opciones, tamOpciones);
	indice = 0;
	memcpy(*opciones, magicCookie, 4);//mete en opciones la magic cookie
	indice += 4;//actualiza el indice a la posicion de siguiente insercion
	memcpy(*opciones + indice, tipoMensaje, 3);//mete el tipo de mensaje
	indice += 3;//actualiza indice posicion
	if (PARAMETRO_ADDRESS != NULL) {
		memcpy(*opciones + indice, direccionPedida, 6);
		indice += 6;
	}

	if (PARAMETRO_HOSTNAME != NULL) {
		memcpy(*opciones + indice, nombreHost, 6);
		indice += 2;
		memcpy(*opciones + indice, PARAMETRO_HOSTNAME, longitudNombreHost);
		indice += longitudNombreHost;
	}
	memcpy(*opciones + indice, opcionLease, 6);
	indice += 6;
	memcpy(*opciones + indice, &finOpciones, 1);
	indice += 1;

	return tamOpciones;
}

int obtenerTamOpcionesRelease(char** opt) {
	int p, tamanyoOpciones;
	u_int32_t auxLease;
	u_int8_t magicCookie[4];
	u_int8_t tipoMensaje[3];
	u_int8_t identificacionServidor[6];
	u_int8_t finOpciones;

	auxLease = PARAMETRO_LEASE;

	magicCookie[0] = 99;
	magicCookie[1] = 130;
	magicCookie[2] = 83;
	magicCookie[3] = 99;

	tipoMensaje[0] = 0x35;
	tipoMensaje[1] = 0x1;
	tipoMensaje[2] = 0x7;

	identificacionServidor[0] = 0x36;
	identificacionServidor[1] = 0x4;
	memcpy(&identificacionServidor[2], &DIRECCION_SERVIDOR, 4);

	finOpciones = 0xFF;

	tamanyoOpciones = 64;
	*opt = malloc(tamanyoOpciones);

	bzero(*opt, tamanyoOpciones);
	p = 0;
	memcpy(*opt, magicCookie, 4);
	p += 4;
	memcpy(*opt + p, tipoMensaje, 3);
	p += 3;
	memcpy(*opt + p, identificacionServidor, 6);
	p += 6;
	memcpy(*opt + p, &finOpciones, 1);
	p += 1;

	return tamanyoOpciones;
}
int obtenerTamOpcionesRequest(char** opciones) {
	int i, p, tamOpciones;
	u_int32_t aux32;
	u_int8_t magicCookie[4];
	u_int8_t tipoMensaje[3];
	u_int8_t direccion[6]; //50
	u_int8_t mascara[6];
	char* listaRouter;
	char* listaDns;
	int tamDireccion;
	u_int8_t identificacionServidor[6];
	u_int8_t nombreHost[2];

	int longitudNombreHost;
	u_int8_t mensajeLease[6];
	u_int8_t finOpciones;

	magicCookie[0] = 99;
	magicCookie[1] = 130;
	magicCookie[2] = 83;
	magicCookie[3] = 99;

	tipoMensaje[0] = 0x35;
	tipoMensaje[1] = 0x1;
	tipoMensaje[2] = 0x3;

	//codigo 50 de rfc2132: requested ip address
	direccion[0] = 50;
	direccion[1] = 4;
	memcpy(&direccion[2], &DIRECCION_SELECCIONADA.s_addr, 4);

	// Subnet Mask: codigo 1 de rfc2132
	mascara[0] = 1;
	mascara[1] = 4;
	memcpy(&mascara[2], &MASCARA_RED->sin_addr.s_addr, 4);

	// Router List
	tamDireccion = sizeof(struct in_addr);
	imprimirDebug("obtenerTamOpcionesRequest", "Tamanyo_lista_routers: %d", TAM_LISTA_ROUTERS);
	listaRouter = malloc(2 + (TAM_LISTA_ROUTERS * tamDireccion));
	listaRouter[0] = 3;//codigo opcion
	listaRouter[1] = TAM_LISTA_ROUTERS*tamDireccion;//numero de direcciones
	//las direcciones una detras de otra
	i = 0;
	imprimirDebug("obtenerTamOpcionesRequest", "Tamanyo_lista_routers: %d", TAM_LISTA_ROUTERS);
	imprimirDebug("obtenerTamOpcionesRequest", "Tamanyo_lista_routers_bytes: %d", 2 + (TAM_LISTA_ROUTERS * tamDireccion));
	while (i < TAM_LISTA_ROUTERS) {
		imprimirDebug("obtenerTamOpcionesRequest", "Trace: %d", 1);
		aux32 = LISTA_ROUTERS[i*tamDireccion].s_addr;
		imprimirDebug("obtenerTamOpcionesRequest", "Trace: %d", 2);
		memcpy(&listaRouter[2+(i*tamDireccion)], &aux32, tamDireccion);
		imprimirDebug("obtenerTamOpcionesRequest", "Trace: %d", 3);
		i++;
	}

	// Domain name List
	imprimirDebug("obtenerTamOpcionesRequest", "Trace: %d, %u, %u", 4, TAM_LISTA_DNS, tamDireccion);
	listaDns = malloc(2 + (TAM_LISTA_DNS * tamDireccion));
	imprimirDebug("obtenerTamOpcionesRequest", "Trace: %d", 5);
	listaDns[0] = 6;//domain name server option
	listaDns[1] = TAM_LISTA_DNS * tamDireccion;//numero de servidores
	//los servidores a continuacion
	i = 0;
	imprimirDebug("obtenerTamOpcionesRequest", "Tamanyo_lista_dns: %d", TAM_LISTA_DNS);
	imprimirDebug("obtenerTamOpcionesRequest", "Tamanyo_lista_dns_bytes: %d", 2 + (TAM_LISTA_DNS * tamDireccion));
	while (i < TAM_LISTA_DNS) {
		aux32 = LISTA_DNS[i].s_addr;
		imprimirDebug("obtenerTamOpcionesRequest", "domain: %u", ntohl(aux32));
		memcpy(&listaDns[2+(i*tamDireccion)], &aux32, tamDireccion);
		i++;
	}

	if (PARAMETRO_HOSTNAME != NULL) {
		longitudNombreHost = strlen(PARAMETRO_HOSTNAME);
		imprimirDebug("obtenerTamOpcionesRequest", "PARAMETRO_HOSTNAME %d",
				longitudNombreHost);
		imprimirDebug("obtenerTamOpcionesRequest", "PARAMETRO_HOSTNAME  = %s",
						PARAMETRO_HOSTNAME);
		nombreHost[0] = 12;//host name option
		nombreHost[1] = longitudNombreHost;
	}

	aux32 = PARAMETRO_LEASE;
	mensajeLease[0] = 51;//ip address lease time
	mensajeLease[1] = 4;
	mensajeLease[5] = aux32;
	aux32 = aux32 >> 8;
	mensajeLease[4] = aux32;
	aux32 = aux32 >> 8;
	mensajeLease[3] = aux32;
	aux32 = aux32 >> 8;
	mensajeLease[2] = aux32;

	identificacionServidor[0] = 54;//server identifier
	identificacionServidor[1] = 4;
	memcpy(&identificacionServidor[2], &DIRECCION_SERVIDOR, 4);

	finOpciones = 0xFF;

	tamOpciones = 64;
	*opciones = malloc(tamOpciones);

	bzero(*opciones, tamOpciones);
	p = 0;
	memcpy(*opciones, magicCookie, 4);
	p += 4;
	memcpy(*opciones + p, tipoMensaje, 3);
	p += 3;

	memcpy(*opciones + p, direccion, 6);
	p += 6;

	memcpy(*opciones + p, mascara, 6);
	p += 6;
	memcpy(*opciones + p, listaRouter, (2 + (TAM_LISTA_ROUTERS * tamDireccion)));
	p += (2 + (TAM_LISTA_ROUTERS * tamDireccion));
	memcpy(*opciones + p, listaDns, (2 + (TAM_LISTA_DNS * tamDireccion)));
	p += (2 + (TAM_LISTA_DNS * tamDireccion));
	if (PARAMETRO_HOSTNAME != NULL) {
		memcpy(*opciones + p, nombreHost, 6);
		p += 2;
		memcpy(*opciones + p, PARAMETRO_HOSTNAME, longitudNombreHost);
		p += longitudNombreHost;
	}
	memcpy(*opciones + p, mensajeLease, 6);
	p += 6;
	memcpy(*opciones + p, identificacionServidor, 6);
	p += 6;
	memcpy(*opciones + p, &finOpciones, 1);
	p += 1;

	free(listaRouter);
	free(listaDns);
	return tamOpciones;
}

char* obtenerTrazaConfiguracionRed(){
	int resultado;
	char *mensaje, *aux;
	resultado = 0;
	aux = malloc(200);
	mensaje = malloc(200);
	bzero(aux, 200);
	bzero(mensaje, 200);
	//Se rellena la ip y el lease;
	if (PARAMETRO_LEASE == 0xffffffff) {
		sprintf(aux, " IP: %s; leasetime: inf;", inet_ntoa(DIRECCION_SELECCIONADA));
	}else{
		sprintf(aux, " IP: %s; leasetime: %u;", inet_ntoa(DIRECCION_SELECCIONADA), PARAMETRO_LEASE);
	}
	//Se rellena la mascara de red
	sprintf(mensaje, "%s subnet mask: %s;",aux, inet_ntoa(MASCARA_RED->sin_addr));

	//Se rellena el router y el hostname del servidor
	if(NOMBRE_SERVIDOR != NULL){
		sprintf(aux, "%s router: %s; server hostname %s;",mensaje, inet_ntoa(LISTA_ROUTERS[0]), NOMBRE_SERVIDOR);
	}else{
		sprintf(aux, "%s router: %s; server hostname -;",mensaje, inet_ntoa(LISTA_ROUTERS[0]));
	}
	//Se rellenan los DNS
	if(TAM_LISTA_DNS >= 1){
		sprintf(mensaje, "%s primary DNS: %s;",aux, inet_ntoa(LISTA_DNS[0]));

		if(TAM_LISTA_DNS >= 2){
			sprintf(aux, "%s secondary DNS: %s",mensaje, inet_ntoa(LISTA_DNS[1]));
		}else{
			sprintf(aux, "%s secondary DNS: -",mensaje);
		}
	}else{
		sprintf(mensaje, "%s primary DNS: -; secondary DNS: -",aux);

	}

	free(mensaje);
	return aux;
}

int pasarCadenaDhcpADatos(struct DatosDhcp * datosDhcp, struct CadenaDhcp *mensajeDhcp){
	int p, longitudOpciones, iaux;
	short saux;
	int resultado = 0;
	unsigned char *mensaje;

	mensaje = mensajeDhcp->contenido;
	longitudOpciones = mensajeDhcp->longitud - DHCP_TAM_BASE;

	p = 0;
	memcpy(&datosDhcp->op, mensaje, 1);
	p += 1;
	memcpy(&datosDhcp->htype, mensaje + p, 1);
	p += 1;
	memcpy(&datosDhcp->hlen, mensaje + p, 1);
	p += 1;
	memcpy(&datosDhcp->hops, mensaje + p, 1);
	p += 1;
	memcpy(&iaux, mensaje + p, 4);
	datosDhcp->xid = ntohl(iaux);
	p += 4;
	memcpy(&saux, mensaje + p, 2);
	datosDhcp->secs = ntohs(saux);
	p += 2;
	memcpy(&saux, mensaje + p, 2);
	datosDhcp->flags = ntohs(saux);
	p += 2;
	memcpy(&iaux, mensaje + p, 4);
	datosDhcp->ciaddr = ntohl(iaux);
	p += 4;
	memcpy(&iaux, mensaje + p, 4);
	datosDhcp->yiaddr = ntohl(iaux);
	p += 4;
	memcpy(&iaux, mensaje + p, 4);
	datosDhcp->siaddr = ntohl(iaux);
	p += 4;
	memcpy(&iaux, mensaje + p, 4);
	datosDhcp->giaddr = ntohl(iaux);
	p += 4;
	memcpy(&datosDhcp->chaddr, mensaje + p, 16);
	p += 16;
	memcpy(&datosDhcp->sname, mensaje + p, 64);
	p += 64;
	memcpy(&datosDhcp->file, mensaje + p, 128);
	p += 128;
	datosDhcp->longitudCampoOpciones = longitudOpciones;
	if (longitudOpciones > 0) {
		datosDhcp->options = malloc(longitudOpciones);
		memcpy(datosDhcp->options, mensaje + p, longitudOpciones);
	}
	return resultado;
}

struct CadenaDhcp* pasarDatosArpACadena(struct DatosArp *datos){
	int tamMensajeArp, p;
	//int iaux;
	short saux;
	unsigned char *mensaje;
	struct CadenaDhcp * resultado;

	resultado = malloc(sizeof(struct CadenaDhcp));

	tamMensajeArp = TAM_PAQ_ARP; //En octetos

	mensaje = malloc(tamMensajeArp);
	p = 0;
	saux = htons(datos->htype);
	memcpy(mensaje, &saux, 2);
	p += 2;
	saux = htons(datos->ptype);
	memcpy(mensaje + p, &saux, 2);
	p += 2;
	memcpy(mensaje + p, &datos->hlen, 1);
	p += 1;
	memcpy(mensaje + p, &datos->plen, 1);
	p += 1;
	saux = htons(datos->oper);
	memcpy(mensaje + p, &saux, 2);
	p += 2;
	memcpy(mensaje + p, &datos->sha, 6);
	p += 6;
	memcpy(mensaje + p, &datos->spa, 4);//ya tiene el endianess correcto
	p += 4;
	memcpy(mensaje + p, &datos->tha, 6);
	p += 6;
	memcpy(mensaje + p, &datos->tpa, 4);//ya tiene el endianess correcto

	resultado->longitud = tamMensajeArp;
	resultado->contenido = mensaje;

	return resultado;
}

struct CadenaDhcp* pasarDatosDhcpACadena(struct DatosDhcp *datos){
	int tamMensajeDhcp, p, iaux;
	short saux;
	unsigned char *mensaje;
	struct CadenaDhcp * resultado;

	resultado = malloc(sizeof(struct CadenaDhcp));

	tamMensajeDhcp = calcularTamMensajeDhcp(datos); //En octetos

	mensaje = malloc(tamMensajeDhcp);
	p = 0;
	memcpy(mensaje, &datos->op, 1);
	p += 1;
	memcpy(mensaje + p, &datos->htype, 1);
	p += 1;
	memcpy(mensaje + p, &datos->hlen, 1);
	p += 1;
	memcpy(mensaje + p, &datos->hops, 1);
	p += 1;
	iaux = htonl(datos->xid);
	memcpy(mensaje + p, &iaux, 4);
	p += 4;
	saux = htons(datos->secs);
	memcpy(mensaje + p, &saux, 2);
	p += 2;
	saux = htons(datos->flags);
	memcpy(mensaje + p, &saux, 2);
	p += 2;
	iaux = htonl(datos->ciaddr);
	memcpy(mensaje + p, &iaux, 4);
	p += 4;
	iaux = htonl(datos->yiaddr);
	memcpy(mensaje + p, &iaux, 4);
	p += 4;
	iaux = htonl(datos->siaddr);
	memcpy(mensaje + p, &iaux, 4);
	p += 4;
	iaux = htonl(datos->giaddr);
	memcpy(mensaje + p, &iaux, 4);
	p += 4;
	memcpy(mensaje + p, &datos->chaddr, 16);
	p += 16;
	memcpy(mensaje + p, &datos->sname, 64);
	p += 64;
	memcpy(mensaje + p, &datos->file, 128);
	p += 128;
	memcpy(mensaje + p, datos->options, datos->longitudCampoOpciones);

	resultado->longitud = tamMensajeDhcp;
	resultado->contenido = mensaje;

	return resultado;
}

void reducirTemporizador(struct timeval *tv, struct timeval *inicio, struct timeval *fin){
	int sec, usec;
	sec = fin->tv_sec - inicio->tv_sec;
	if(fin->tv_usec >= inicio->tv_usec){
		usec = fin->tv_usec - inicio->tv_usec;
	}else{
		sec -= 1;
		usec = 1000000 +(fin->tv_usec-inicio->tv_usec);
	}
	tv->tv_sec = TEMPORIZADOR_SEC - sec;
	if(TEMPORIZADOR_USEC >= usec){
		tv->tv_usec = TEMPORIZADOR_USEC - usec;
	} else {
		if (tv->tv_sec <= 0) {
			tv->tv_sec = 0;
			tv->tv_usec = 0;
		} else {
			tv->tv_sec -= 1;
			tv->tv_usec = 1000000 + (TEMPORIZADOR_USEC - usec);
		}
	}
	imprimirDebug("reducirTemporizador","TO %d.%06d - res %d.%06d = tv %d.%06d ",TEMPORIZADOR_SEC,TEMPORIZADOR_USEC, sec, usec,tv->tv_sec,tv->tv_usec);
}

int operandoEnBound(){
	int sal=-1;
	int tiempoRestante;
	/* Cuando se llame a este metodo el valor de LEASE debe ser conocido
	 * y viene del mensaje ACK que se ha recibido del servidor
	 * conocido el tiempo de lease se coge el T1 y T2
	 */
	iniciarTiemposBound();

	tiempoRestante = TEMP_TOTAL_T1 - TEMPORIZADOR_AUX;
	imprimirDebug("ejecutar","tiempo restante BOUND %d", tiempoRestante);
	//como se ignoran los mensajes q se reciben en este periodo pues se duerme el tiempo T1
	sleep(TEMP_TOTAL_T1);
	//enviarRequest(FALSE);
	enviarRequestUDP();
	sal=1;
	return sal;
}

void iniciarTiemposBound(){
	TEMPORIZADOR_AUX = 4;
	/* cojo los valores de Lease, T1 y T2 */
	double tiempoTotal=PARAMETRO_LEASE;
	TEMP_TOTAL_T1=tiempoTotal*0.5;
	TEMP_SEC_T1=TEMP_TOTAL_T1/1000;
	TEMP_USEC_T1=(TEMP_SEC_T1*1000)  - TEMP_TOTAL_T1;
	TEMP_TOTAL_T2=tiempoTotal*0.875;
	TEMP_SEC_T2=TEMP_TOTAL_T2/1000;
	TEMP_USEC_T2=(TEMP_SEC_T2*1000)  - TEMP_TOTAL_T2;
}

void obtenerSiguienteTemporizadorT1(struct timeval *tv){
	double  r;
	r = (((double)rand()/(double)RAND_MAX)*2)-1;
	TEMP_SEC_T1 = (TEMPORIZADOR_AUX + r);
	TEMP_USEC_T1 = ((TEMPORIZADOR_AUX + r) - TEMP_SEC_T1) *1000000;
	imprimirDebug("obtenerSiguienteTemporizadorT1","r %e, AT %d, ST %d, UT %d",r, TEMPORIZADOR_AUX, TEMP_SEC_T1, TEMP_USEC_T1);
	tv->tv_sec = TEMP_SEC_T1;
	tv->tv_usec = TEMP_USEC_T1;
	TEMPORIZADOR_AUX *=2;
}

int enviarRequestUDP(){
	int resultado = EXIT_ERROR;
	struct DatosDhcp* dhcpRequest;
	struct CadenaDhcp * cadena;
	char ** opciones;
	int tamOpciones;
	char * msgString;
	int aux;

	msgString = malloc(260);
	bzero(msgString, 60);
	imprimirDebug("enviarRequestUDP", "enviando dhcpRequestUDP");

	/* Se crea e inicializa la estructura del mensaje */
	dhcpRequest = crearNuevoDhcp();
	dhcpRequest->op = OP_BOOTREQUEST;
	dhcpRequest->htype = HTYPE_ETHERNET;
	dhcpRequest->hlen = 6;
	dhcpRequest->xid = XID;
	dhcpRequest->secs = 0;
	aux = htonl(DIRECCION_SELECCIONADA.s_addr);
	memcpy(&dhcpRequest->ciaddr, &aux, sizeof(in_addr_t));//copia la direccion en el nuevo DatosDhcp que se va a enviar como DHCPRequest
	memcpy(dhcpRequest->chaddr, DIRECCION_HW, dhcpRequest->hlen);

	opciones = malloc(4);
	tamOpciones = obtenerTamOpcionesRequest(opciones);
	dhcpRequest->options = (*opciones);
	dhcpRequest->longitudCampoOpciones = tamOpciones;
	cadena = pasarDatosDhcpACadena(dhcpRequest);
/*
	if(broadc==TRUE){
		// Se envia el mensaje dhcp request al servidor
		if(enviarMensajeUdp(cadena->contenido, cadena->longitud,(struct in_addr*)INADDR_BROADCAST)>0){ //aqui es enviar por UDP y a una direccion
			//OJO IMPORTANTE: si hay un fallo de memoria puede ser aqui, no deberia porq in_addr no usa punteros pero
			// hay q estar pendiente
			struct in_addr a;
			a.s_addr=INADDR_BROADCAST;
			imprimirTraza(XID, DHCPREQUEST, inet_ntoa(a));
			resultado = TRUE;
		} else {
			fprintf(stderr,
					"ERROR: No se ha podido mandar el mensaje dhcpRequest.\n");
		}
	}else{
*/
		// Se envia el mensaje dhcp request al servidor por UDP
		if(enviarMensajeUdp(cadena->contenido, cadena->longitud, &DIRECCION_SERVIDOR)>0){
			//sprintf(msgString, "%s (renewing", inet_ntoa(DIRECCION_SERVIDOR));
			//sprintf(msgString, " %s)", inet_ntoa(DIRECCION_SELECCIONADA));
			strcat(msgString,inet_ntoa(DIRECCION_SERVIDOR));
			strcat(msgString, " (renewing ");
			strcat(msgString, inet_ntoa(DIRECCION_SELECCIONADA));
			strcat(msgString, ")");
			//sprintf(msgString, "%s (renewing %s)", inet_ntoa(DIRECCION_SERVIDOR), inet_ntoa(DIRECCION_SELECCIONADA));
			imprimirTraza(XID, DHCPREQUEST, msgString);
//			imprimirTraza(XID, DHCPREQUEST, inet_ntoa(DIRECCION_SERVIDOR));
			resultado = TRUE;
		} else {
			fprintf(stderr,
					"ERROR: No se ha podido mandar el mensaje dhcpRequest.\n");
		}
//	}
	free(msgString);
	free(opciones);
	liberarDatosDhcp(dhcpRequest);
	liberarCadenaDhcp(cadena);
	return resultado;
}

/*
 * Se opera en el estado Renewing
 * Se envio por UDP una peticion de renovacion de IP (Request) y ahora se esta a la espera de un mensaje ACK o NACK
 */
int operandoEnRenewing(){
	fd_set recvset; //conjunto de descriptores de entrada
//	struct timeval tv, inicio, fin; //intervalo maximo de espera
	struct timeval tv; //intervalo maximo de espera
	int resultado = 1;
	char * buf = malloc(1000);
	//uint buf_ocupation = 0;
	struct DatosDhcp dhcpRecibido;
	int ack = -1;
	char tempmsg[50];
	// se inicializan los descriptores
	FD_ZERO(&recvset); //inicializa el conjunto de descriptores
	FD_SET(descriptorSocketDatagramasUDP, &recvset); //incluye el descriptorSocketNivelEnlace en el conjunto

//	obtenerSiguienteTemporizador(&tv);
	tv.tv_sec = 0.375 * PARAMETRO_LEASE;
	tv.tv_usec = 0;

	imprimirDebug("obtenerMensajeAck", "Timeout sec:%d, usec:%d", tv.tv_sec,
			tv.tv_usec);
//	gettimeofday(&inicio, NULL);
	resultado = 1;
	while (resultado > 0 && ack == -1) {
		resultado = select(descriptorSocketDatagramasUDP + 1, &recvset, NULL, NULL, &tv);//resultado=0 si el tiempo se agota
//		gettimeofday(&fin, NULL);
//		reducirTemporizador(&tv, &inicio, &fin);
		imprimirDebug("obtenerMensajeAck", "Timeout sec:%d, usec:%d", tv.tv_sec, tv.tv_usec);
		imprimirDebug("obtenerMensajeAck", "resultado %d", resultado);
		if(resultado < 0) {
			perror("select");
		} else if(resultado> 0) {
			int recv_size = recvfrom(descriptorSocketDatagramasUDP, buf, 1000, 0, NULL, NULL);
			imprimirDebug("obtenerMensajeAck", "recibido %d",recv_size);

			obtenerCabDhcpDesdeUDP(&dhcpRecibido, buf, recv_size);
			if(dhcpRecibido.xid == XID) {
				ack = esAck(&dhcpRecibido);//ack=1 => DHCPACK, ack=0 => recibido DHCPNAK
				if(ack == -1){
					imprimirDebug("obtenerMensajeAck", "El paquete ack no es válido\n");
				}

				imprimirDebug("obtenerMensajeAck", "ipOrigen %d",dhcpRecibido.siaddr);
				imprimirDebug("obtenerMensajeAck", "id %d",dhcpRecibido.xid);
			} else {
				imprimirDebug("obtenerMensajeAck", "Distinto Xid");
			}

			if(dhcpRecibido.longitudCampoOpciones> 0) {
				free(dhcpRecibido.options);
			}
		}
	}
	bzero(tempmsg, 50);
	if(ack == 0) {
		imprimirTraza(dhcpRecibido.xid, DHCPNAK, NULL);
	}
	else if(ack == 1) {
		if(PARAMETRO_LEASE == 0xffffffff) {
			sprintf(tempmsg, "%s with leasing inf seconds",inet_ntoa(DIRECCION_SELECCIONADA));
		}else{
			sprintf(tempmsg, "%s with leasing %u seconds", inet_ntoa(DIRECCION_SELECCIONADA), PARAMETRO_LEASE);
		}
		imprimirTraza(dhcpRecibido.xid, DHCPACK, tempmsg);
	}
	resultado = ack;
	if((ack =! -1) && (dhcpRecibido.longitudCampoOpciones> 0)){
		imprimirDebug("obtenerMensajeAck", "free options");
		free(dhcpRecibido.options);
	}
	imprimirDebug("obtenerMensajeAck", "free buf");
	free(buf);
	imprimirDebug("obtenerMensajeAck", "fin");
	return resultado;
}

/*
 * Obtiene el mensaje ACK o NACK del servidor, caso especifico de Renewing
 * 1 en caso de ACK, 0 en caso de NACK, -1 en caso de error o de no leer nada
 *
 */
int obtenerMensajeAckRenewing() {
	fd_set recvset; //descriptores de entrada
	struct timeval tv, inicio, fin; //intervalo de espera
	int resultado = 1;
	char * buf = malloc(1000);
	struct DatosDhcp dhcpRecibido;
	int ack = -1;
	int tamanyoPaquete;
	char tempmsg[50];

	/* Inicializando los sets de descriptores */
	FD_ZERO(&recvset); //inicializa el conjunto de descriptores
	FD_SET(descriptorSocketNivelEnlace, &recvset); //pone el descriptorSocketNivelEnlace

	obtenerSiguienteTemporizadorT1(&tv);
	imprimirDebug("obtenerMensajeAck", "Timeout sec:%d, usec:%d", tv.tv_sec,
			tv.tv_usec);
	gettimeofday(&inicio, NULL);

	/* Es posible obtener mas de una respuesta y es necesario analizarlas todas hasta tener una respuesta */
	resultado = 1;
	while (resultado > 0 && ack == -1) {
		resultado = select(descriptorSocketNivelEnlace + 1, &recvset, NULL, NULL, &tv);//resultado=0 si el limite de tiempo expira
		gettimeofday(&fin, NULL);
		reducirTemporizadorT1(&tv, &inicio, &fin);
		imprimirDebug("obtenerMensajeAck", "Timeout sec:%d, usec:%d", tv.tv_sec, tv.tv_usec);
		imprimirDebug("obtenerMensajeAck", "resultado %d", resultado);
		if(resultado < 0) {
			perror("select");
		} else if(resultado> 0) {
			// Lee un buffer de 1000 bytes
			int recv_size = recvfrom(descriptorSocketNivelEnlace, buf, 1000, 0, NULL, NULL);
			imprimirDebug("obtenerMensajeAck", "recibido %d",recv_size);

			tamanyoPaquete = obtenerLongPaqIp(buf, recv_size);//
			if(esUdp(buf, recv_size) == 0){//
				if(esDhcp(buf, recv_size) == 0){//
					if(obtenerCabDhcpDesdeMensIp(&dhcpRecibido, buf, recv_size) > 0){//obtenerCabDhcp
						// Se comprueba que el mensaje responda al ultimo Xid
						if(dhcpRecibido.xid == XID) {
							ack = esAck(&dhcpRecibido);//ack=1 => DHCPACK, ack=0 => recibido DHCPNAK
							if(ack == -1){
								imprimirDebug("obtenerMensajeAck", "El paquete ack no es válido\n");
							}

							imprimirDebug("obtenerMensajeAck", "ipOrigen %d",dhcpRecibido.siaddr);
							imprimirDebug("obtenerMensajeAck", "id %d",dhcpRecibido.xid);
						} else {
							imprimirDebug("obtenerMensajeAck", "Distinto Xid");
						}

						if(dhcpRecibido.longitudCampoOpciones> 0) {
							free(dhcpRecibido.options);
						}
					}
				}
				else {
					imprimirDebug("obtenerMensajeAck", "El paquete no es dhcp\n");
				}
			}
			else {
				imprimirDebug("obtenerMensajeAck", "El paquete no es udp\n");
			}
		}
	}
	bzero(tempmsg, 50);
	if(ack == 0) {
		imprimirTraza(dhcpRecibido.xid, DHCPNAK, NULL);
	}
	else if(ack == 1) {
		if(PARAMETRO_LEASE == 0xffffffff) {
			sprintf(tempmsg, "%s with leasing inf seconds",inet_ntoa(DIRECCION_SELECCIONADA));
		}else{
			sprintf(tempmsg, "%s with leasing %u seconds", inet_ntoa(DIRECCION_SELECCIONADA), PARAMETRO_LEASE);
		}
		imprimirTraza(dhcpRecibido.xid, DHCPACK, tempmsg);
	}
	resultado = ack;
	if((ack =! -1) && (dhcpRecibido.longitudCampoOpciones> 0)){
		imprimirDebug("obtenerMensajeAck", "free options");
		free(dhcpRecibido.options);
	}
	imprimirDebug("obtenerMensajeAck", "free buf");
	free(buf);
	imprimirDebug("obtenerMensajeAck", "fin");
	return resultado;
}

/* Reduce el temporizador asociado a la variable temporal AUX y T1 */
void reducirTemporizadorT1(struct timeval *tv, struct timeval *inicio, struct timeval *fin){
	int sec, usec;
	sec = fin->tv_sec - inicio->tv_sec;
	if(fin->tv_usec >= inicio->tv_usec){
		usec = fin->tv_usec - inicio->tv_usec;
	}else{
		sec -= 1;
		usec = 1000000 +(fin->tv_usec-inicio->tv_usec);
	}
	tv->tv_sec = TEMP_SEC_T1 - sec;
	if(TEMP_USEC_T1 >= usec){
		tv->tv_usec = TEMP_USEC_T1 - usec;
	} else {
		if (tv->tv_sec <= 0) {
			tv->tv_sec = 0;
			tv->tv_usec = 0;
		} else {
			tv->tv_sec -= 1;
			tv->tv_usec = 1000000 + (TEMP_USEC_T1 - usec);
		}
	}
	imprimirDebug("decrease_timeout","TO %d.%06d - res %d.%06d = tv %d.%06d ",TEMP_SEC_T1,TEMP_USEC_T1, sec, usec,tv->tv_sec,tv->tv_usec);
}

/* Inicializa los temporizadores para la parte final */
void inicializarTemporizadores2() {
	TEMPORIZADOR_AUX = 4;
	TEMP_SEC_T1 = 0;
	TEMP_USEC_T1 = 0;
}


/* Ejecuta las operaciones del estado Rebinding */
int operandoEnRebinding(){
	int sal=0;
	int hayACK=-2;
//	int contestado=FALSE;

	inicializarTemporizadores2();
	/*
	 * El tiempo de ejecucion en este estado debe ser la diferencia entre el tiempo de prestamo
	 * y el que hemos consumido representado por el tiempo T2
	 */
//	int tiempoDiferencia=PARAMETRO_LEASE-TEMP_TOTAL_T2;
//	int tiempoRestante= tiempoDiferencia-TEMPORIZADOR_AUX;
//	inicializarTemporizadores2();
//	tiempoRestante = tiempoDiferencia - TEMPORIZADOR_AUX;
//	while(tiempoRestante>0 && !contestado){
//		tiempoRestante = tiempoDiferencia - TEMPORIZADOR_AUX;
		hayACK=obtenerMensajeAckRebinding();
		//0, se recibe un noACK, 1 si se recibe un ACK, -1 sino se recive nada
//		if(hayACK >= 0){
//			contestado=TRUE;
//		}
//	}
	if(hayACK==1){ //ACK
		sal=1;
	}else if(hayACK==0){ //NACK
		sal=0;
	}else{ //No se ha leido nada
		sal=-1;
	}
	return sal;
}

/* convierte una cadena (mensaje UDP) en una estructra de datos de tipo DHCP */
int obtenerCabDhcpDesdeUDP(struct DatosDhcp * dhcp, char * ip_msg, int ip_msg_len) {
	struct CadenaDhcp dhcpM;
	int resultado = 0;

	dhcpM.contenido = (unsigned char *) ip_msg;
	dhcpM.longitud = ip_msg_len;
	imprimirDebug("obtenerCabDhcpDesdeUDP", "el paquete es dhcp\n");

	if(resultado == -1){
		imprimirDebug("obtenerCabDhcpDesdeUDP", "el paquete no es dhcp\n");
		return -1;
	}

	if(pasarCadenaDhcpADatos(dhcp, &dhcpM) >= 0)
		return ip_msg_len;
	return -1;
}


int enviarRequestUDPBroadcast(){
	int resultado = EXIT_ERROR;
	struct DatosDhcp* dhcpRequest;
	struct CadenaDhcp * cadena;
	char ** opciones;
	int tamOpciones;
//	char * msgString;
	int aux;

//	msgString = malloc(60);
//	bzero(msgString, 60);
	imprimirDebug("enviarRequestUDPBroadcast", "enviando dhcpRequestUDPbroadcast");

	/* Se crea e inicializa la estructura del mensaje */
	dhcpRequest = crearNuevoDhcp();
	dhcpRequest->op = OP_BOOTREQUEST;
	dhcpRequest->htype = HTYPE_ETHERNET;
	dhcpRequest->hlen = 6;
	dhcpRequest->xid = XID;
	dhcpRequest->secs = 0;
	aux = htonl(DIRECCION_SELECCIONADA.s_addr);
	memcpy(&dhcpRequest->ciaddr, &aux, sizeof(in_addr_t));//copia la direccion en el nuevo DatosDhcp que se va a enviar como DHCPRequest
	memcpy(dhcpRequest->chaddr, DIRECCION_HW, dhcpRequest->hlen);

	opciones = malloc(4);
	tamOpciones = obtenerTamOpcionesRequest(opciones);
	dhcpRequest->options = (*opciones);
	dhcpRequest->longitudCampoOpciones = tamOpciones;
	cadena = pasarDatosDhcpACadena(dhcpRequest);

	// Se envia el mensaje dhcp request al servidor
	if(enviarMensajeUdpBroadcast(cadena->contenido, cadena->longitud)>0){ //aqui es enviar por UDP y a una direccion
		//OJO IMPORTANTE: si hay un fallo de memoria puede ser aqui, no deberia porq in_addr no usa punteros pero
		// hay q estar pendiente
		struct in_addr a;
		a.s_addr=INADDR_BROADCAST;
//		sprintf(msgString, "all (rebinding %s)", inet_ntoa(DIRECCION_SELECCIONADA));
//		imprimirTraza(XID, DHCPREQUEST, msgString);
//		imprimirTraza(XID, DHCPREQUEST, inet_ntoa(a));
		resultado = TRUE;
	} else {
		fprintf(stderr,	"ERROR: No se ha podido mandar el mensaje dhcpRequest en enviarRequestUDPBroadcast().\n");
	}

//	free(msgString);
	free(opciones);
	liberarDatosDhcp(dhcpRequest);
	liberarCadenaDhcp(cadena);
	return resultado;
}

int enviarMensajeUdpBroadcast(unsigned char* mensaje, uint longitud) {
	struct sockaddr_in socketDestino;
	struct sockaddr_in socketOrigen;
	int resultado = 0;
	int activado = 1;
	char * msgString;

	msgString = malloc(60);
	bzero(msgString, 60);

	descriptorSocketDatagramasUDP = socket(AF_INET,SOCK_DGRAM, 0);

	setsockopt(descriptorSocketDatagramasUDP, SOL_SOCKET, SO_BROADCAST, &activado, sizeof(int));
	if (descriptorSocketDatagramasUDP < 0)
		perror("socket, enviarMensajeUdp");
	else {
		struct in_addr estAux;
		estAux.s_addr=INADDR_BROADCAST;
		socketDestino.sin_addr = estAux;
		socketDestino.sin_family = AF_INET;
		socketDestino.sin_port = htons(PUERTO_DHCP_SERVIDOR);

		socketOrigen.sin_addr = DIRECCION_SELECCIONADA;
		socketOrigen.sin_family = AF_INET;
		socketOrigen.sin_port = htons(PUERTO_DHCP_CLIENTE);
			resultado = sendto(descriptorSocketDatagramasUDP, mensaje, longitud, 0, (struct sockaddr*) &socketDestino,
					sizeof(struct sockaddr_in));
			if (resultado < 0) {
				perror("sendto, enviarMensajeUdp");
			}else{
				sprintf(msgString, "all (rebinding %s)", inet_ntoa(DIRECCION_SELECCIONADA));
				imprimirTraza(XID, DHCPREQUEST, msgString);
//				imprimirTraza(XID, DHCPREQUEST, inet_ntoa(estAux));
			}
	}
	setsockopt(descriptorSocketDatagramasUDP, SOL_SOCKET, SO_REUSEADDR, &activado, sizeof(int));

	free(msgString);

	return resultado;
}
